Welcome to Pete Brown's 10rem.net

First time here? If you are a developer or are interested in Microsoft tools and technology, please consider subscribing to the latest posts.

You may also be interested in my blog archives, the articles section, or some of my lab projects such as the C64 emulator written in Silverlight.

(hide this)

Using Pub/Sub and the Observer Pattern in Silverlight 2 (Part 1.5 of 2) More on Service Call Chaining

Pete Brown - 27 June 2008

In my previous post, I discussed how to chain service calls. One question I received on that was how to do the opposite: take an action after all the calls (or by extension, some logical groups of calls) have completed.

This is one of the challenges with working with async service calls. Rather than have a single function that makes a bunch of inline service calls one after the other, you need to fire off a bunch of calls, which will probably return out of order, and make some decision only after all have completed.

Making async calls is recommended for most client applications. A WPF application, for example, could fire off a bunch of calls and get a response to the user more quickly than it could if it chained the calls inline in a single function. In Silverlight 2, however, it is not only a good idea, it is a mandate due to the architecture of the network client.

So, how do you handle making a number of service calls and then taking an action when they've all completed? There are a few ways to do this, one of which (option 2 below) I'm using in the Silverlight 2 Beta 2 application I'm writing right now.

Option 1: Combined Observer and Data Loading Class

This is the simplest option, but doesn't scale out well when working with multiple disparate data sources. The advantage here is simplicity in initial coding. In this case, the DataLoadingPublisher (which is likely but not necessarily what is both making the service calls and containing the properties which hold the data) gets all the data and then publishes an event indicating that all the data is loaded. The subscriber listens for that specific event and takes action when it catches it.

image

You'd still publish the Data1Loaded and Data2Loaded events in the event bus, but I left them out to keep the diagram uncluttered.

The code for this is pretty straight-forward, but I'm not going to include it here, as I personally feel Option 2 is the better option in almost all cases.

Option 2: Use Dedicated Observer Class

This option injects another class into the mix, but provides the ability to have a loosely-coupled observer outside the data sources. This option is good if you have multiple sources of data coming from multiple interfaces to the outside world.

As before, the Event Bus is our common publish/subscribe location.

The various publishers publish DataXLoaded events as shown in the previous post. The Data Watcher listens for those events and keeps track internally of which data has been loaded and which hasn't. Once it identifies that all data has been loaded, it publishes its own AllDataLoaded event, which other classes (pages etc.) can subscribe to.

image

Here's what the code might look like:

public static class DataWatcher
{

    public static DataWatcher()
    {
        EventBus.ConfigurationLoaded += new EventHandler(SomeDataLoaded);
        EventBus.EventPageContentLoaded += new EventHandler(SomeDataLoaded);
        EventBus.GeneralPageContentLoaded += new EventHandler(SomeDataLoaded);
        EventBus.MediaPageContentLoaded += new EventHandler(SomeDataLoaded);
        EventBus.UserInformationLoaded += new EventHandler(SomeDataLoaded);
    }

    static void SomeDataLoaded(object sender, EventArgs e)
    {
        if (ApplicationData.Current.Configuration != null &&
            ApplicationData.Current.EventPageContent != null &&
            ApplicationData.Current.GeneralPageContent != null &&
            ApplicationData.Current.MediaPageContent != null &&
            ApplicationData.Current.UserInformation != null)
        {
            OnAllDataLoaded(ApplicationData.Current, new EventArgs());
        }

    }
    private static void OnAllDataLoaded(object sender, EventArgs e)
    {
        EventBus.OnAllDataLoaded(sender, e);
    }
}

I'm not using dedicated flags in this case; I'm using the fact that data values are null to indicate loaded/not loaded state. As that is not always practical, here's another approach that uses dedicated flag variables to indicate progress. This is a more robust as it doesn't require knowledge of the internal operations of the ApplicationData class, but instead relies only on its notification events.

public static class DataWatcher
{
    private static bool _configurationLoaded = false;
    private static bool _eventPageContentLoaded = false;
    private static bool _generalPageContentLoaded = false;
    private static bool _mediaPageContentLoaded = false;
    private static bool _userInformationLoaded = false;

    public static DataWatcher()
    {
        EventBus.ConfigurationLoaded += new EventHandler(EventBus_ConfigurationLoaded);
        EventBus.EventPageContentLoaded += new EventHandler(EventBus_EventPageContentLoaded);
        EventBus.GeneralPageContentLoaded += new EventHandler(EventBus_GeneralPageContentLoaded);
        EventBus.MediaPageContentLoaded += new EventHandler(EventBus_MediaPageContentLoaded);
        EventBus.UserInformationLoaded += new EventHandler(EventBus_UserInformationLoaded);
    }

    #region Event Subscriptions

    static void EventBus_UserInformationLoaded(object sender, EventArgs e)
    {
        _userInformationLoaded = true;
        CheckForAllDataLoaded();
    }

    static void EventBus_MediaPageContentLoaded(object sender, EventArgs e)
    {
        _mediaPageContentLoaded = true;
        CheckForAllDataLoaded();
    }

    static void EventBus_GeneralPageContentLoaded(object sender, EventArgs e)
    {
        _generalPageContentLoaded = true;
        CheckForAllDataLoaded();
    }

    static void EventBus_EventPageContentLoaded(object sender, EventArgs e)
    {
        _eventPageContentLoaded = true;
        CheckForAllDataLoaded();
    }

    static void EventBus_ConfigurationLoaded(object sender, EventArgs e)
    {
        _configurationLoaded = true;
        CheckForAllDataLoaded();
    }

    #endregion

    #region Checks for All Data Loaded

    private static void CheckForAllDataLoaded()
    {
        if (_configurationLoaded &&
            _eventPageContentLoaded &&
            _generalPageContentLoaded &&
            _mediaPageContentLoaded &&
            _userInformationLoaded)
        {
            OnAllDataLoaded(Application.Current, new EventArgs());
        }
    }

    private static void OnAllDataLoaded(object sender, EventArgs e)
    {
        EventBus.OnAllDataLoaded(sender, e);
    }

    #endregion

}

While this works well, what this doesn't do is allow for later clearing and updates to the data. To support that in the previous example, the ApplicationData class would simply null out all existing data and start reloading. The check for null would work as it should, but would require cooperation from the ApplicationData class - coupling we probably shouldn't have.

Instead, in this new example, the ApplicationData class could publish a DataCleared event when it clears its data in order to reload, and the DataWatcher would subscribe to that and set the flags appropriately. If the ApplicationData class simply overwrites the data and never provides any indication that the checks need to be reset, the model breaks. That is the one bit of cooperation that this still requires, and is reasonable IMHO.

You can extend this example to include classes of data or groups of data which should be loaded as a whole, and notify based on that. Instead of AllDataLoaded, you might have UserDataLoaded and AllPageDataLoaded etc.

The async service model presents some challenges in Silverlight, but the advantages are numerous, and the patterns you follow to work with it are pretty well-understood.

I hope this helps you out with your current and future Silverlight projects. Look for another post soon on using the patterns in these two posts to handle UI navigation.

             
posted by Pete Brown on Friday, June 27, 2008
filed under:              

2 comments for “Using Pub/Sub and the Observer Pattern in Silverlight 2 (Part 1.5 of 2) More on Service Call Chaining”

  1. Nicosays:
    Thanks a lot Pete, this was just what I was after. Option 2 indeed seems the best option (because one of the calls can fail, leaving the result to 'null' every time...).

    Will implement this on monday :-)

Comment on this Post

Remember me

3 trackbacks for “Using Pub/Sub and the Observer Pattern in Silverlight 2 (Part 1.5 of 2) More on Service Call Chaining”

  1. POKE 53280,0: Pete Brown's Blogsays:
    I was going to present an analogy using different types of cars, but more often than not, the analogy
  2. Community Blogssays:
    I was going to present an analogy using different types of cars, but more often than not, the analogy
  3. POKE 53280,0: Pete Brown's Blogsays:
    My primary Silverlight project for the past couple months has been the Facebook application I’ve been