Rockford Lhotka
    CTO at Magenic
    Author, speaker, software architect
    Creator of the CSLA .NET framework

About me
Contact me


Lazy or deferred loading of child objects


Lazy loading (or deferred or on-demand loading) of a child object is relatively easy to implement in CSLA .NET.

Make sure to apply this errata before proceeding.

The idea is that a child object or child collection is not loaded from the database when the root object is loaded, but rather on the first (if any) attempt by client code to access the child data. This is only a good idea if the typical usage of the parent object does not require the child data. If the child data actually gets used it is far more efficient to load all the data at once than to use lazy loading. However, if 80% of the time the child data is not used then lazy loading can make a lot of sense.

For instance, if we have a Contact root object, which has a collection of child Detail objects that are rarely accessed then we can use lazy loading on the Detail collection.

Implementing this requires a bit of attention on our part as developers because we need to add extra code any time we direct access the Private field holding the child collection (or object) reference. Any time we reference this variable, we need to see if it is Nothing, and if so we need to trigger a load operation.

The exception is in the DataPortal_Update method, where if the child variable is Nothing then we simply ignore it. If the data was never loaded, then there's nothing to update.

To pursue the Contact/Detail example, in the Contact class we'd have a variable to store the child collection:

Private mDetails As Details = Details.NewDetails()

We'd also have a property to expose this to clients, where we'd use lazy loading:

Public ReadOnly Property Details() As Details
    If mDetails Is Nothing Then
      mDetails = Details.GetDetails(Me.ID)
    End If
    Return mDetails
  End Get
End Property

In many cases, this is the only code in the parent object that deals with the mDetails variable, and if so then we should be all set. If there is other code that directly accesses the mDetails variable, it should be changed to use the Details property instead, thus centralizing the lazy loading operation. If that is not possible, then the lazy loading code will need to be duplicated anywhere the mDetails variable is used.

We then need to make sure that we don't have any Details in the case that we've loaded the Contact object from the database. This requires a change to the DataPortal_Fetch in Contact. In pseudo-code it is like this:

Protected Overrides Sub DataPortal_Fetch(ByVal Criteria As Object)
  ' load Contact data as normal
  ' instead of loading Details, we set it to Nothing
  mDetails = Nothing
End Sub

By setting mDetails to nothing, we ensure that subsequent attempt to access this information will trigger the lazy load operation. Notice that we didn't load the data here, we just defer the load until sometime in the future (maybe).

The only other change is in the DataPortal_Update of the Contact class. In pseudo-code it is like this:

Protected Overrides Sub DataPortal_Update()
  ' save Contact data as normal
  If Not mDetails Is Nothing Then
  End If
End Sub

We only update the child data if we have actually loaded child data.


(Updated 4/11/2003)