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.
Figure 1 – A layered client-side Silverlight architecture
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
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.
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 repositoryif (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.
Leave a Reply