Querying WCF RIA Services and handling async results

I had a good question in the comments of Part 2 of my series on WCF RIA Services that I thought I would answer here so more could find the information.

To paraphrase the question: “How can I retrieve a set of entities into my client and process them when the retrieval is complete?

I didn’t go into detail on the latter part of this in the article because of space limitations. But basically the answer is that you can be notified of the completion of any async operation (Load and SubmitChanges primarily) that you call on a RIA Services domain context. There are really two programming models, but I’ll just show one for now as the other is very similar.

When you call DomainContext.Load, RIA services executes the retrieval in the background and the call is non-blocking to the calling thread. But often you need to get those results, do some processing on them, and then move on to make another query or do something else.

An easy way to handle things is to use an overload of Load (and SubmitChanges) that takes a callback delegate that will be called when the async work is complete. This model is slightly different than the two other familiar async patterns in .NET: BeginXXX/EndXXX method pairs and XXXAsync/XXXCompleted method/event.

In the RIA Services async pattern, you can pass an Action delegate to the async method you are calling, and that target method will be executed when the async operation is complete. It will pass you results and any error information if applicable.

In the case of a Load call, the code would look something like this:

privatevoid RetrieveAndProcessTasks() { TasksDomainContext context = new TasksDomainContext(); EntityQuery query = context.GetTasksQuery(); Action> completeProcessing = delegate(LoadOperation loadOp) { if (!loadOp.HasError) { ProcessTasks(loadOp.Entities); } else { LogAndNotify(loadOp.Error); } }; LoadOperation loadOperation = context.Load(query.Where(t=>t.EndDate > DateTime.Now),completeProcessing,null); } privatevoid ProcessTasks(IEnumerable entities) { // Do what you need to do } privatevoid LogAndNotify(Exception error) { // Log the error and notify user if appropriate }

Before calling Load on the domain context, you set up an Action> delegate to point to a handling method with the signature void TargetMethod(LoadOperation op). In this example I do that with an anonymous method that checks whether there are any errors, and if not hands off to a processing method the entities that were returned. If there is an error, it hands off the Exception to a handling method as well.

Then it is just a matter of calling Load and passing that delegate as a second argument (the first is always the EntityQuery you want to execute), and a final argument that is an arbitrary state object, similar to the Begin/End signatures of the original .NET async pattern.

Unfortunately there is no easy way to shield you entirely from the async nature of service calls in Silverlight if you want to do something other than data bind to the results immediately after the load occurs.