Donnerstag, 24. Juni 2010

Synchronizing multiple WCF Service Calls

The scenario is as follows: assume you have a service that delivers articles. It has two methods: GetArticleList, which returns a list of all articles, and GetSpecialOfferArticle which returns a special offer for a given month and year. You want to display the articles in a listbox and higlight the special offer. The point is, that within the client you first need to get the result of GetAllArticles and then the result of GetSpecialOfferArticle. When we do the service call asynchrounsly (like we must do in silverlight), the return order is not guaranteed.

So the article introduces a class called ServiceCallSynchornizer to handle the problem. The idea is to register multiple service calls, cache the results and get notified by an event once all calls are completed. The case described above would then look like:


private ServiceCallSynchronizer sync; 
private ArticleServiceClient proxy ;

private void DoServiceCalls()
{
  proxy = new ArticleServiceClient();
  sync = = new ServiceCallSynchronizer();

  sync.AllCompleted += new EventHandler<EventArgs>(sync_AllCompleted);
  sync.AddServiceCall(new Action<Object>(proxy.GetArticleListAsync), "ArticleList");
  sync.AddServiceCall(new Action<int, int, Object>(proxy.GetSpecialOfferArticleAsync), DateTime.Now.Month, DateTime.Now.Year, "SpecialOffer");
  
  sync.ExecuteAll();
}

private void sync_AllCompleted(object sender, EventArgs e)
{
  ObservableCollection<Article> articles = sync.GetResult<GetArticleListCompletedEventArgs>("ArticleList").Result;
  Article specialOffer = sync.GetResult<GetSpecialOfferArticleCompletedEventArgs>("SpecialOffer").Result;

  ArticleList.ItemsSource = articles;
  ArticleList.SelectedItem = (from art in articles where art.Number == specialOffer.Number select art).FirstOrDefault();
}

Within the DoServiceCalls method we register for the AllCompleted event of the ServiceCallSynchronizer. This event is called when all registered service calls are completed.
Service calls are added using the AddServiceCallMethod. We must pass a delegate into this method as well as the call paramters. This way, the method name and parameters are defined type safe and a check wether the method name exists or not can be done at compile time. The parameter values though are passed as Object[], so they are not type safe at design time, which is a little drawback. But the developer can see the needed parameters within the Action<> definition so it should be ok.
The last parameter passed into the AddServiceCall methods ("ArticleList" / "SpecialOffer" in the example above) are unique keys that we can use to identify the result of our service calls in the AllCompleted event handler. They are mapped to the UserState parameter of the underlying service proxy method.
The usage of this keys can be seen in the sync_AllCompleted method. Using the ServiceCallSynchonizers GetResult<T> method we can obtain the service result by passing in the appropriate key. Because we get notified when all calls are completed, I can use my results in the needed order.

The code can be found here. As usual provided as-is with no warrenties.

The provided solution might help you if you use WCF. If you are free to use other technologies you might choose the Microsoft CCR or the CodePlex project XCoordination Application Space. Ralf Westphal blogged about it and presents these alternatives in detail.

Have fun!

Keine Kommentare:

Kommentar veröffentlichen