— New online course!  Getting Started with Azure DevOps — Special launch price 50% off —

Silverlight Asynchronous WCF Calls Without Ruining Your Architecture

by

A while back I wrote a blog post about how Silverlight 4’s asynchronous network calls make a layered client-side architecture difficult.  In that post I talk about wanting to have a loosely coupled, testable, n-tier architecture on my Silverlight client application.  Basically, I wanted to have a client-side architecture that was nicely layered like Figure 1 rather than a flat, 1-tier architecture like Figure 2. 

image
Figure 1 – A layered client-side Silverlight architecture

 

image
Figure 2 – A flat client-side Silverlight architecture

Although I didn’t write it in that original post, unit testing and unit testability was one of my top reasons for wanting a layered architecture.  If everything is clumped in to one big XAP/DLL, it’s going to be difficult to test small “units” of functionality.  My top priority was to keep the WCF data access code (aka. the code that calls the back-end WCF services) separated from the rest of my code behind Repository Pattern interfaces.  Keeping those Repositories separated and interface-driven is essential for eliminating unit test dependencies on those running WCF services. 

A Change In Plans

When I wrote that original post back in May of 2010, I was *fighting* the asynchronous WCF network calls.  After a little bit of time, I had one of those moments where I asked myself “self, why is this so *&#$@!& difficult?  There’s no way that this is right.  I think we’re doing something wrong here.”  I’ve since changed my approach and I’ve embraced the asynchronous nature of Silverlight and make it a core part of my architecture.

The Problem with WCF Data Access

Let’s take the Repository Pattern.  The Repository Pattern is an encapsulation of logic to read and write to a persistent store such as a database or the file system. If you can write data to it, pull the plug on your computer, and you’re still be able to read that data the next time that you start your computer, it’s a persistent store.  When you’re writing a Silverlight application that talks to WCF Services, your persistent store is your WCF Service. 

If you look at IRepository<T> or IPersonRepository in Figure 3, you’ll see that the GetById() and GetAll() methods return actual, populated IPerson instances.  This is how it would typically look in a non-Silverlight, non-async implementation.  Some other object decides it wants Person #1234, gets an instance of IPersonRepository, calls GetById(), passes in id #1234, waits for the GetById() call to return, and then continues going about it’s business.  In this case, the call to GetById() is a “blocking call”.  The code calls IPersonRepository.GetById, the thread of execution enters the repository implementation for GetById(), does its work, and then the repository issues a “return returnValue;” statement that returns the populated value, and the thread of execution leaves the repository.  While the thread of execution is inside of the repository, the caller is doing nothing else other than waiting for the return value. 

Figure 3 – Server-side, non-Silverlight Repository Pattern implementation
Figure 3 – Server-side, non-Silverlight Repository Pattern implementation

In Silverlight, if your Repository calls a WCF service to get it’s data, this *CAN NOT BE IMPLEMENTED LIKE THIS* unless you do something complex like what I did in my previous post (translation: unless you do something that’s probably wrong).  The reason is that the majority of the work that happens in the IPersonRepository.GetById() method is actually going to happen on a different thread from the thread that called the GetById() method.  IPersonRepository.GetById() will initiate the process of loading that IPerson but it won’t be responsible for finishing it and won’t even know when the call has been finished.  This means that IPersonRepository.GetById() can’t return an instance of IPerson anymore…it now has to return void

I know what you’re thinking.  You’re *sure* that I’m wrong.  Give it a second.  Think it over.  It’s kind of a mind-bender.  Re-read the paragraph a few more times.  Maybe sketch some designs out on paper.  Trust me, I’m right. 

These asynchronous WCF calls mean that none of your repositories can return anything other than void.  Think this out a little bit more.  This also means that anything that in turn calls a WCF Repository is also not permitted to return anything but void.  It’s insidious and it destroys your plans for a layered architecture (Figure 1) and pushes you closer to the “lump” architecture (Figure 2). 

A New Hope

Letting these asynchronous WCF calls ruin my layered architecture was simply unacceptable to me.  What I needed was to get something like return values but still be async-friendly.  I also needed a way to handle exceptions in async code, too.  If you think about it, exceptions are kind of like return values.  If an exception happens on the thread that’s doing the async work, because there isn’t a continuous call stack from the client who requested the work to the thread that’s throwing the exception, there’s no way to get the exception back to the original caller because throwing exceptions relies on the call stack.  No blocking call, no continuous call stack.

Enter ReturnResult<T>.  This class encapsulates the connection between the original caller and the asynchronous thread that does the work. 

image
Figure 4 – ReturnResult<T>

An implementation of IPersonRepository that uses ReturnResult<T> would look something like the code in Figure 5.  The key point here is that when the WCF service call is finished and the GetByIdCompleted() method is executed, the repository has access to the ReturnResult<T> through the callback variable.  When the Repository has something to return to the caller – either an exception or an instance of IPerson – it calls the Notify() method on ReturnResult<T>.  From there the ReturnResult<T> class takes care of the rest and the Repository doesn’t need to know anything about it’s caller.

public class WcfPersonRepository : IPersonRepository
{       
    public void GetById(ReturnResult<IPerson> callback, int id)
    {
        PersonServiceClient service = null;

        try
        {
            // create an instance of the WCF PersonServiceClient proxy
            service = new PersonServiceClient();
               
            // subscribe to the completed event for GetById()
            service.GetByIdCompleted +=
                new EventHandler<GetByIdCompletedEventArgs>(
                    service_GetByIdCompleted);

            // call the GetById service method
            // pass “callback” as the userState value to the call
            service.GetByIdAsync(id, callback);
        }
        catch
        {
            if (service != null) service.Abort();
            throw;
        }
    }

    void service_SearchCompleted(object sender, GetByIdCompletedEventArgs e)
    {
        var callback = e.UserState as ReturnResult<IList<IPerson>>;

        if (callback == null)
        {
            // this is bad
            throw new InvalidOperationException(
                “e.UserState was not an instance of ReturnResult<IPerson>.”);
        }

        if (e.Error != null)
        {
            // something went wrong
            // “throw” the exception up the call stack
            callback.Notify(e.Error);
            return;
        }

        try
        {
            IPerson returnValue;

            // do whatever needs to be done to create and populate an
            // instance of IPerson
               
            callback.Notify(returnValue);
        }
        catch (Exception ex)
        {
            callback.Notify(ex);
        }
    }
}

Figure 5 – Asynchronous IPersonRepository implementation with ReturnResult<T>

Now, let’s look at an example of something that calls IPersonRepository.  A common example of this would be PersonViewModel needing to call in to the IPersonRepository to get a person to display.  In Figure 6 you can see this implemented and the key line is repository.GetById(new ReturnResult<IPerson>(LoadByIdCompleted), id);.  This line creates an instance of ReturnResult<IPerson>() and passes in a callback delegate to the ViewModel’s LoadByIdCompleted() method.  This is how the ViewModel waits for return values from the Repository without having to wait while the thread of execution is blocked. 

public class PersonViewModel
{       
    public void LoadById(int id)
    {
        IPersonRepository repository = GetRepositoryInstance();

        // initiate the call to the repository
        repository.GetById(
            new ReturnResult<IPerson>(LoadByIdCompleted), id);
    }

    private void LoadByIdCompleted(ReturnResult<IPerson> result)
    {
        // handle the callback from the repository

        if (result == null)
        {
            throw new InvalidOperationException(“Result was null.”);
        }
        else if (result.Error != null)
        {
            HandleTheException(result.Error);
        }
        else
        {
            IPerson person = result.Result;

            if (person == null)
            {
                // person not found…invalid id?
                Id = String.Empty;
                FirstName = String.Empty;
                LastName = String.Empty;
                EmailAddress = String.Empty;
            }
            else
            {
                Id = person.Id;
                FirstName = person.FirstName;
                LastName = person.LastName;
                EmailAddress = person.EmailAddress;
            }
        }
    }

    // the rest of the implementation details go here
}

Figure 6 – PersonViewModel calls IPersonRepository.GetById()

Summary

ReturnResult<T> solved my asynchronous Silverlight problems and allowed me to keep a layered and unit testable architecture by encapsulating all of the asynchronous “glue” code. 

Click here to download the sample code. 

 

Looking for help with your Silverlight architecture?  Need some help implementing your asynchronous WCF calls?  Hit a wall where you need to chain two asynchronous calls together?  Drop us a line at info@benday.com


5 Responses to "Silverlight Asynchronous WCF Calls Without Ruining Your Architecture"
  1. Thanks Benjamin,
    I started developing in Silverlight just a few months ago (from an ASP .Net background), and I’m having difficulties wrapping my head around the Async concept. Your post helped me fix my immediate problem (calling and returning WCF values in a layered architecture). I’m far from being comfortable in this subject, but your post is making me see some light at the end of the tunnel!

  2. It certainly looks like a good way to fight asynchrony. But is it still usefull when you need to ‘stack’ multiple wcf-calls together (and in the end still have the same ReturnResult?)
    The different calls have a different returnType and so a different ReturnResult.
    Or am I thinking too hard?

  3. Joop, are you asking about changing the return type for a bunch of chained calls? Since ReturnResult uses the same T through all those chained calls, it’s a little difficult (read: probably impossible) to change the T halfway through the chain of calls.

    What’s probably happening is that you probably need another object in there somewhere. When I have a complex set of async operations that have to happen in a particular order, I wrap them in an object. I usually name this something like “XyzOperationCoordinator”. The object that initiates the XyzOperationCoordinator passes in a ReturnResult that it wants a Notify(T) call on when the coordinated operation completes. Inside of the OperationCoordinator, that object can do a ton of different stuff against a ton of different services with a ton of different T’s — everything is clean and the only rule is that you call Notify() on the original ReturnResult that initiated the OperationCoordinator.

    Does this help?

    -Ben

  4. Thanks for the quick reply. Indeed that looks like a neat solution. Wrap them in an object and when all calls are succesfully completed one can call returnresult.Notify. And along all the different calls you have the correct .notify(exception) available.
    let’s try..

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: