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.
Here’s a link to download the sample code.
-Ben
Update 12/24/2010 – Check out my follow up to this post.
Leave a Reply