Blendable MVVM: WCF and Asynch Data Sources
Blendable MVVM
In the previous posts we've been retrieving our data from a synchronous data source, for the most part in Silverlight we'll be retrieving data sources from aseparate server, in this case through a WCF Service. Silverlight has a restriction that all network calls must be asynchronous in order to avoid locking up the browser UI (a good thing). It does however make the code a little more complicated.
The functionality of the CocktailsService will stay the same, we're just going to migrate it over to the server and host it as a WCF service. The code for the service is remains exactly the same, just shifted now to the server and hosted as a WCF service. If you'd like to learn more about setting up a WCF service you can read more on MSDN.
[ServiceContract]
public interface ICocktailService
{
[OperationContract]
IEnumerable<Cocktail> GetCocktails();
[OperationContract]
IEnumerable<Cocktail> GetCocktailsSimilarTo(Cocktail cocktail);
}
Then we generate a client proxy using Visual Studios "Add Service Reference". The proxy generated includes any types from the data contracts (the Cocktail class), the proxy client (CocktailsServiceClient) and a service interface (ICocktailsService). One thing to notice if you've done any WCF work before is that the interface doesn't contain the events exposed by the actual client class.
We'll now need to alter our Cocktails view model to deal with the new interface. Basically it's a relatively simple change from synchronous methods to using async ones. It should look like this...
public class CocktailsViewModel : ViewModelBase<CocktailsViewModel>
{
private readonly ICocktailService cocktailService;
public CocktailsViewModel(ICocktailService cocktailService)
{
this.cocktailService = cocktailService;
AvailableCocktails = new ObservableCollection<Cocktail>();
SimilarCocktails = new ObservableCollection<Cocktail>();
GetSimilarCocktailsCommand = new DelegateCommand<Cocktail>(GetSimilarCocktails);
cocktailService.BeginGetCocktails(OnGetCocktails, null);
}
private void OnGetCocktails(IAsyncResult ar)
{
var cocktails = cocktailService.EndGetCocktails(ar);
AvailableCocktails.AddRange(cocktails);
}
public ObservableCollection<Cocktail> AvailableCocktails
{
get;
set;
}
public ObservableCollection<Cocktail> SimilarCocktails
{
get;
set;
}
public ICommand GetSimilarCocktailsCommand
{
get;
private set;
}
protected void GetSimilarCocktails(Cocktail cocktail)
{
cocktailService.BeginGetCocktailsSimilarTo(cocktail, OnGetCocktailsSimilarTo, null);
}
private void OnGetCocktailsSimilarTo(IAsyncResult ar)
{
var cocktails = cocktailService.EndGetCocktailsSimilarTo(ar);
SimilarCocktails.Replace(cocktails);
}
}
There is a slight problem with the code above. Silverlight expects property changed notifications (including collection changed from ObservableCollection) to be on the UI thread. The async result methods will be on a separate thread (The generated service client will dispatch the OnXCompleted events back to the UI thread but not the asynch methods). There a couple of ways we can solve this.
- Manually dispatch any updates to properties back to the UI thread.
- Create our own ObservableCollection that ensures all notifications are on the correct thread. An example of this can be found at "Adding to an ObservableCollection from a background thread"
For this example we'll just be going with the first. We'll update our ViewModelBase to take a reference to the Application dispatcher and have a simple helper method.
protected ViewModelBase()
{
Dispatcher = Deployment.Current.Dispatcher;
}
protected Dispatcher Dispatcher
{
get; private set;
}
protected void InvokeOnUIThread(Action action)
{
Dispatcher.BeginInvoke(action);
}
We then alter the offending methods to use the helper.
private void OnGetCocktails(IAsyncResult ar)
{
var cocktails = cocktailService.EndGetCocktails(ar);
InvokeOnUIThread(() => AvailableCocktails.AddRange(cocktails));
}
private void OnGetCocktailsSimilarTo(IAsyncResult ar)
{
var cocktails = cocktailService.EndGetCocktailsSimilarTo(ar);
InvokeOnUIThread(() => SimilarCocktails.Replace(cocktails));
}
Our updates to the collections will be dispatched off to the UI thread and the exception will be avoided.

Comments
Rcldlubx - Wednesday, November 11, 2009
comment3
Djnyvjrb - Wednesday, November 11, 2009
comment1
Zxbzkrzs - Wednesday, November 11, 2009
comment4
Romibkpe - Wednesday, November 11, 2009
comment4
Ftybevlg - Wednesday, November 11, 2009
comment5
Pvobltar - Wednesday, November 11, 2009
comment5
Pruafltb - Wednesday, November 11, 2009
comment2
Wlztncbs - Thursday, November 12, 2009
comment2
Isqvvfcz - Thursday, November 12, 2009
comment3
Ajcfmzvu - Thursday, November 12, 2009
comment6
Zsqaayug - Thursday, November 12, 2009
comment5
Txgynyxq - Thursday, November 12, 2009
comment2
Nzhuoxqg - Thursday, November 12, 2009
comment4
Gmhfpuzh - Thursday, November 12, 2009
comment1
Ixvuxnje - Thursday, November 12, 2009
comment3
Zowrelwd - Thursday, November 12, 2009
comment2
Ebptuszk - Thursday, November 12, 2009
comment5
Uxkyyjbp - Thursday, November 12, 2009
comment6
Jpnqcwjt - Friday, November 13, 2009
comment4
Zdmktvrv - Friday, November 13, 2009
comment5
Ivldihyg - Friday, November 13, 2009
comment6
Iuaxicuj - Friday, November 13, 2009
comment5
Ufgeynau - Saturday, November 14, 2009
comment3
Xpefvmis - Sunday, November 15, 2009
comment3
Zfwxuhtj - Sunday, November 15, 2009
comment3
Llmtrnzz - Sunday, November 15, 2009
comment4
Ugqpoubm - Sunday, November 15, 2009
comment1
Kltifpeq - Sunday, November 15, 2009
comment6
Xeljpnjo - Sunday, November 15, 2009
comment6
Nurrfquu - Sunday, November 15, 2009
comment4
Fpbnunxf - Sunday, November 15, 2009
comment2
Zqcmcnxq - Sunday, November 15, 2009
comment1
Xseuhegz - Monday, November 16, 2009
comment3
Vihuuica - Monday, November 16, 2009
comment4
Sapxxwpu - Monday, November 16, 2009
comment2
Auhtkwtg - Thursday, November 19, 2009
Jonny was here