Update 12/24/2010 – Check out my follow up to this post. 

Silverlight is great.  Silverlight 4 is extra great.  If you know a little bit about WPF and a handful about the ViewModel Pattern, you’re in great shape.  If you’re like me, you tend to mock up your user interfaces and user interface functionality using only client-side functionality before you start to make calls back to the server-side WCF services for save/update/load operations.  This mocking strategy is great for iterating with your customer (thinking Scrum/Agile) and allows you to get a “working” user interface before you commit to implementing all the back-end business functionality. 

Well, if this is your first Silverlight application, there’s a catch.  Yah.  A big ol’ catch.  As you work from the UI back toward the service, you’re eventually going to need to start making calls to those WCF services from Silverlight.  All those calls are asynchronous.  It’s not optional either – they really have to be.  If you don’t make those calls asynchronously, the Silverlight UI locks up like a…uhhmmm…like a…uhmmm…it locks up like a blogger trying to come up with a clever simile for a completely broken and completely hung Silverlight user interface. 

So, think about the structure of an Async call in .NET: you make a call to an async method and you provide that async call with a delegate to call when it’s done.  Essentially, you tell the async method to call you back when it’s done.  That call you make to the async method is non-blocking and returns immediately.  Essentially the async method is saying “I’ll be back later.  Don’t wait up.” 

Think about what this means: any async call to a WCF service will *always* return void to its caller.

If your service calls are simple – you make one call up and you get a response back – then you’re probably not sweating this. 

If you like to keep your service calls and your business logic separated from your ViewModel and Presentation logic then you’re probably starting to sweat a little bit.  Why?  Because your user interface layer is going tend to be your integration point and, if you have to set any return values on to the UI from that service call, you’re probably going to have to provide a callback delegate to something in the user interface tier.  (Ohhh…yah…not pretty.)  Yah…that’s an n-tier violation. 

If you need to make multiple service calls in order to make a decision to populate something in your UI or ViewModel then you’re really in trouble. 

Here’s an example of what I mean. Let’s say you want to Save some Order data back to the server and that your WCF services require you to call a IsLoggedIn() to determine if you’re logged in BEFORE you call the Save() method. 

Step 1: User clicks the Save button the UI and the OrderFacade.SaveOrder() method gets called
Step 2: The OrderFacade SaveOrder() method wraps all the logic required to do the save.  It calls up to the server to check whether you’re logged in.  The IsLoggedIn() WCF method returns either true or false. 
Step 3: If IsLoggedIn() returns false then call Login() with some locally cached credentials
Step 4: If you’re logged in successfully, call Save() on the WCF service
Step 5: When Save() is done, return the new Order number to the caller of OrderFacade.SaveOrder()

If this were done using non-async (synchronous) service calls, this would be completely easy.  Each call to the service would block until the call returned, execution would fall to the next line, you’d make your decision using an if statement and call the next service, etc.  It’s just regular ordinary programming. 

public class OrderFacade
{
    public int SaveOrder(OrderData saveThis)
    {
        // check if user is logged in
        var securityClient = new SecurityServiceClient();
        try
        {
            if (securityClient.IsLoggedIn() == false)
            {
                LogIn();
            }

            securityClient.Close();
        }
        catch (Exception)
        {
            if (securityClient != null) securityClient.Abort();

            throw;
        }
        // make the call to save the order
        var orderClient = new OrderServiceClient();

        try
        {
            int orderId = orderClient.Save(saveThis);

            orderClient.Close();

            // return the new order id
            return orderId;
        }
        catch (Exception)
        {
            if (orderClient != null) orderClient.Abort();

            throw;
        }
    }

    private void LogIn()
    {
        // go make some service calls to log in
    }
}

In asynchronous programming, none of those calls up to the server block so it’s virtually impossible to string together a bunch of calls and make decisions based on the results. 

The Solution

Well, Razan Paul gave me an idea in his blog post about Silverlight 3.  He uses a combination of System.Threading.Thread and System.Threading.AutoResetEvent to make emulate some synchronous calls to WCF from Silverlight.  The basic idea is that you create an AutoResetEvent as a member variable and then use it to stop your thread’s execution and wait for a notification from another thread to resume processing. 

Here’s what the SaveOrder() code looks like with the AutoResetEvent:

public class OrderFacadeWithSynchronousEmulation
{
    private AutoResetEvent m_autoResetEvent = new AutoResetEvent(false);     

    public int SaveOrder(OrderData saveThis)
    {
        // check if user is logged in
        var securityClient = new SecurityServiceClient();

        securityClient.IsLoggedInCompleted +=
            new EventHandler<EventArgs>(securityClient_IsLoggedInCompleted);

        // make call to async IsLoggedIn() service method
        securityClient.IsLoggedInAsync();
        // stop our thread until we get notified by the callback
        m_autoResetEvent.WaitOne();

        // after m_autoResetEvent.Set() is called by callback,
        // the flow of execution resumes here

        // do more stuff
    }

    void client_WhatTimeIsItCompleted(object sender, EventArgs e)
    {
        m_autoResetEvent.Set();
    }
}

Return Values From the Service

Now what if you want to emulate a value being returned from the service?  Well, all those Async() methods that you have to calling the WCF services have an overload that takes “object userState” as a parameter.  This method parameter lets you pass anything you want in to the call and then access that same object instance from the Callback method. 

Enter AsyncCallStatus<T>. 

public class AsyncCallStatus<T>
{
    public AsyncCallStatus()
    {

    }

    public T CompletedEventArgs { get; set; }
}

All the callback event handler methods get passed an instance of some kind of EventArgs class.  The event args for the callback have all the return values and error values that come back from the WCF service call.  I created AsyncCallStatus<T> so that I had a reusable object that I could use to move the event args (and all the WCF data) between the calling function and the callback method. 

So if you have an async method call like this that passes in an instance of AsyncCallStatus<>:

var whatTimeIsItStatus = new AsyncCallStatus<TimeService.WhatTimeIsItCompletedEventArgs>();
client.WhatTimeIsItAsync(whatTimeIsItStatus);
m_autoResetEvent.WaitOne();

you can have a callback handler like this an then get access to that same call status through the eventargs parameter’s UserState property.  Then all you have to do is set the completed eventargs reference on to AsyncCallStatus’s CompletedEventArgs property, call m_autoResetEvent.Set(), and you’re done. 

void client_WhatTimeIsItCompleted(object sender, TimeService.WhatTimeIsItCompletedEventArgs e)
{
    var status = e.UserState as AsyncCallStatus<WhatTimeIsItCompletedEventArgs>;

    status.CompletedEventArgs = e;

    m_autoResetEvent.Set();
}

When the WaitOne() call resumes, the AsyncCallStatus object instance is populated with the result of the WCF call.

var whatTimeIsItStatus = new AsyncCallStatus<TimeService.WhatTimeIsItCompletedEventArgs>();
client.WhatTimeIsItAsync(whatTimeIsItStatus);
m_autoResetEvent.WaitOne();

if (whatTimeIsItStatus.CompletedEventArgs.Error != null)
{
    throw whatTimeIsItStatus.CompletedEventArgs.Error;
}

DateTime currentTime = whatTimeIsItStatus.CompletedEventArgs.Result;

What does this mean for the user interface?

You’ve broken free of the tyranny of the asynchronous WCF calls!  Cool, huh?

Not so fast. 

You can still lock up your user interface if you call this method from say a Silverlight button click handler.  Why?  Because that blocking call from the AutoResetEvent.WaitOne() is going to be executing on the Silverlight UI thread.  (whoops!)  That WaitOne() call is pretty much the definition of a hung UI. 

Don’t despair.  There’s a workaround for this, too.  Fire off the call from your click handler using ThreadPool.QueueUserWorkitem().  This gets the work off the UI thread by making it execute on a thread in the ThreadPool.  Getting the data set back onto the UI is then done using Deployment.Current.Dispatcher.BeginInvoke(). (Thanks to Shawn Wildermuth for the BeginInvoke() idea.)  Dispatcher.BeginInvoke() marshals the work that has to be done from whatever thread you’re running on to the user interface thread.  (NOTE: if you try to manipulate any UI control from a thread other than the UI thread, you’ll get an exception.)

private void m_btnUpdate_Click(object sender, RoutedEventArgs e)
{
    try
    {
        ThreadPool.QueueUserWorkItem(delegate(object o)
        {
            TimeInformationViewModel viewModel = new TimeInformationViewModel();

            new TimeFacade().UpdateTimeInfo(viewModel);

            Deployment.Current.Dispatcher.BeginInvoke(() => this.DataContext = viewModel);
        });
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

Sample Code

Anyway, I wrote a quick sample application in Visual Studio 2010 & Silverlight 4 that demonstrates all these concepts.  The app calls up to a WCF service to get the current time on the server.  When the Silverlight client gets the server time, it takes the ticks value and calls back up to the server to find out if the ticks value is odd or even.  When that returns, the UI gets populated with the data. 

image

Here’s a link to download the sample code.

-Ben

Update 12/24/2010 – Check out my follow up to this post. 

Share →

4 Responses to Silverlight 4 Synchronous WCF Service Calls (or “How to avoid writing a 1-tier Silverlight App”)

  1. AM says:

    It is actually very possible to call WCF "synchronously" in SL4 and without blocking the UI and it’s a bit simpler than this. Hopefully the blogger will take notice of this comment.

    Do use ThreadPool.QueueUserWorkItem to call a method on your business facade. The implemented method will look like this:

    var syncResult = this.Channel.BeginLogin(username, password, null, null);
    asyncResult.AsyncWaitHandle.WaitOne();
    var result = this.Channel.EndLogin(asyncResult);

    The call to AsyncWaitHandle.WaitOne() returned by the service itself will cause the thread to block until the call is done. This keeps your facade interface synchronous while calling the facade from the ThreadPool keeps your UI running.

  2. bob kennelly says:

    Hello Ben, thanks for the excellent write up, its exactly the kind of situation i’m in right now where i’m continually locking up my laptop when developing and have to hard boot to get back to where i left off!

    What i’m wondering Ben is if there is something i can implant into the code or other that will allow me to shut down the program using the task manager and not have to continually hard boot my laptop?

    Thanks again Ben!
    Bob Kennelly

  3. Prasanna says:

    Thanks a lot Ben! Your example was really helpful. Keep it up.

  4. Neelesh says:

    Hello,
    I was facing the same problem in making async calls to RIA service query. So can I use the following code as you have suggested above?

    LoadOperation lop = _context.Load(_context.GetInformationQuery());

    m_autoResetEvent.WaitOne();

    and the the load completed event

    void lop_Completed(object sender, EventArgs e)
    {
    m_autoResetEvent.Set();
    }

    However, when I use above method, my UI hangs for ever.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>