Thursday, July 01, 2010
« Silverlight 4 VisualStateManager Valid i... | Main | CSLA 4 release candidate available »

The fact that server access is all async in Silverlight makes some common tasks a little more challenging. This includes lazy loading child objects in an on-demand manner.

When implementing lazy loading in CSLA it is pretty typical to implement the on-demand load of the child property value in the child property getter. In other words, in the parent object, there’ll be a property that exposes the child object, and in the getter of that property is where the lazy loading code goes:

public static PropertyInfo<Child> ChildProperty = RegisterProperty<Child>(c => c.Child);
public Child Child
{
  get
  {
    if (!FieldManager.FieldExists(ChildProperty))
    {
      Child = DataPortal.Fetch<ChildLoader>().Child;
      OnPropertyChanged(ChildProperty);
    }
    return GetProperty(ChildProperty);
  }
  private set { LoadProperty(ChildrenProperty, value); }
}

The ChildLoader type shown here is a command object (I typically use a ReadOnlyBase subclass) that runs to the server, loads the child object and returns it to the client. ChildLoader might look like this:

[Serializable]
public class ChildLoader : ReadOnlyBase<ChildLoader>
{
  public static PropertyInfo<Child> ChildProperty = RegisterProperty<Child>(c => c.Child);
  public Child Child
  {
    get { return ReadProperty(ChildProperty); }
    private set { LoadProperty(ChildProperty, value); }
  }

  private void DataPortal_Fetch()
  {
    Child = DataPortal.FetchChild<Child>();
  }
}

This all works great in .NET, where all this code is synchronous. But it needs a little tweaking to work in Silverlight, where server communication is async.

First, the parent property getter must return something before it has the actual value. This is kind of a big deal, because it means the UI must be able to handle getting back a null value, and then later getting the real value. This isn’t too hard though, at least if you are using data binding, because the UI will show nothing for the null value, and will automatically refresh the display when the real value arrives (because CSLA will raise a PropertyChanged event for the Child property).

Also, you’ll probably want to mark the parent object as busy while the operation is occurring, so the UI can take appropriate steps (again, by using data binding against the IsBusy property) to block the user from trying to interact with this part of the app until the actual data arrives:

public static PropertyInfo<Child> ChildProperty = RegisterProperty<Child>(c => c.Child);
public Child Child
{
  get
  {
    if (!FieldManager.FieldExists(ChildProperty))
    {
      MarkBusy();
      DataPortal.BeginFetch<ChildLoader>((o, e) =>
        {
          if (e.Error != null)
            throw e.Error;
          Child = e.Object.Child;
          MarkIdle();
          OnPropertyChanged(ChildProperty);
        });
    }
    return GetProperty(ChildrenProperty);
  }
  private set { LoadProperty(ChildrenProperty, value); }
}

When the getter is first called it will start the async load operation, and then it will return null. Later, when the BeginFetch() call completes it will set the Child property to the actual value and indicate that the parent object is no longer busy.

The ChildLoader is unaffected, since it was (and still is) running primarily on the .NET app server. The only difference is that the client is using BeginFetch() instead of Fetch(), so the ChildLoader is running async instead of sync. But that doesn’t affect the way ChildLoader works – so the only impact is to the parent object’s Child property getter.

Thursday, July 01, 2010 3:34:18 PM (Central Standard Time, UTC-06:00)
Correct me if I'm wrong, but shouldn't the setter for the Child property be using SetProperty rather than LoadProperty in order to trigger the NotifyPropertyChangedEvent. In the case of a ReadOnlyBase<T> object you would call OnPropertyChanged(ChildrenProperty) immediately after LoadProperty(ChildrenProperty, value). Unless of course something has changed in the latest versions I'm unaware of.
Anthony Langlois
Friday, July 02, 2010 1:33:20 AM (Central Standard Time, UTC-06:00)
Good catch Anthony, thanks! Though I typically don't call SetProperty() because I don't want all the authz and other rules processing - so I just call OnPropertyChanged() - and I've updated the blog post accordingly.
Friday, July 02, 2010 12:28:22 PM (Central Standard Time, UTC-06:00)
I am not sure, but does this mean it works for UI bindings only? What if you access the property in the business layer? Then you can't use the property getter. You need something like
BeginGetChild((o, e)=>
{
....
}
And even for UI binding you may need an additional flag that is set when you start the asynchronous operation and reset when the operation completes. Otherwise, if the binding is triggering the property getter again before the initial asynchronous operation completes, you're in trouble….
Andreas Kuhlmann
Sunday, July 04, 2010 1:57:06 PM (Central Standard Time, UTC-06:00)
Hmm, with multiple properties being fetched a MarkIdle will mark the business object idle while another property is still fetching. I solved it by MarkPropertyBusy(..) and MarkPropertyIdle(..) foreach property. Another solution would had been increments on MarkBusy.
Raymond de Jong
Monday, July 05, 2010 12:02:50 PM (Central Standard Time, UTC-06:00)
In Sergey's post http://forums.lhotka.net/forums/p/6752/32445.aspx#32445 regarding lazy loaded properties in Silverlight he uses a pubic non static variable to control reentry into the async call.

If you combine Sergey's post with this one and provide a compile directive to support synchronous calls you will get the following. Using the following pattern will allow you to call properties in the business layer satisfying Andreas Kuhlmann's issue.

private bool m_loadingChild = false;
public static PropertyInfo<Child> ChildProperty = RegisterProperty<Child>(c => c.Child);
public Child Child
{
get
{
if (!m_loadingChild && (!FieldManager.FieldExists(ChildProperty) || ReadProperty<Child>(ChildProperty) == null))
{
#if !SILVERLIGHT
Child = DataPortal.Fetch<ChildLoader>().Child;
OnPropertyChanged(ChildProperty);
#else
m_loadingChild = true;
MarkBusy();
DataPortal.BeginFetch<ChildLoader>((o, e) =>
{
if (e.Error != null)
throw e.Error;
Child = e.Object.Child;
MarkIdle();
OnPropertyChanged(ChildProperty);
m_loadingChild = false;
});
#endif
}
return GetProperty<Child>(ChildrenProperty);
}

private set { LoadProperty<Child>(ChildrenProperty, value); }
}
Chris Dufour
Monday, July 05, 2010 4:13:41 PM (Central Standard Time, UTC-06:00)
A couple of thoughts about Chris's solution:
- A race condition has been introduced with the m_loadingChild variable that could be resolved through the user of the Interlocked class. Unfortunately Interlocked.CompareExchange doesn't have an overload that supports the Boolean type so you'd have to use integers/const values such as const int TRUE = 1 and const int FALSE = 0.
- As was pointed out in Raymond's post the use of the generic MarkIdle and MarkBusy properties might not be the best route to take
- Instead of using the SILVERLIGHT compilation direction you could use a partial class and a partial method with a separate file for the implementation of the silverlight and non-silverlight version. The partial method would wrap the contents of the respective sections of the compiler directives.
Anthony Langlois
Comments are closed.