Using the Visual Studio Async CTP with RIA Services


I’ve been excited about this CTP for quite a while now and it’s great to finally have a chance to play with it. Way back in early RIA days we had a number of conversations with the TPL (Task Parallel Library) folks to see if we could use Task<T> with RIA. We were never quite able to sort out the details so now that I’ve finally had a chance to use the two together I’m pretty excited.

Using Async on the Server

As soon as you pick up the Async CTP, you get the idea it can be used everywhere. Asynchronous code becomes just as easy to write as synchronous code, and  the performance improvements it offers are tantalizing . It’s all still pretty new, but I’m guessing you’ll find reasons to use it in your DomainServices pretty quickly.

Right now, all DomainService operations are synchronous, so it’s up to you to marry the two worlds. Luckily, Task makes this pretty easy to accomplish. For instance, let’s say you want to use an asynchronous method that returns a Task. To consume it synchronously, all you have to do is write a synchronous method that Waits for the task to complete.

  using RGB =
System.Tuple<SampleRedEntity, SampleGreenEntity, SampleBlueEntity>; [EnableClientAccess()] public class SampleDomainService : DomainService { public IEnumerable<SampleRedEntity> GetRedEntities() { return this.GetEntitiesSync().Select(t => t.Item1); } public IEnumerable<SampleGreenEntity> GetGreenEntities() { return this.GetEntitiesSync().Select(t => t.Item2); } public IEnumerable<SampleBlueEntity> GetBlueEntities() { return this.GetEntitiesSync().Select(t => t.Item3); } private RGB[] GetEntitiesSync() { Task<RGB[]> task = this.GetEntitiesAsync(); task.Wait();
      return task.Result;
    }

    private async Task<RGB[]> GetEntitiesAsync() {…}
  }

Using Async in Silverlight

In Silverlight, using Async becomes a little more interesting for two reasons; (1) You’re already forced to use async, and (2) RIA Services provides an async model similar to Task<T>.

My first step in integrating the CTP with RIA was to create a simple extension method to consume our RIA operations (LoadOperation<T>, etc.) as Tasks. I played around with different variations, but in the end it turned out to be quite simple.

  public static class OperationExtensions
  {
    public static Task<T> AsTask<T>(this T operation)
where T : OperationBase { TaskCompletionSource<T> tcs =
new TaskCompletionSource<T>(operation.UserState); operation.Completed += (sender, e) => { if (operation.HasError && !operation.IsErrorHandled) { tcs.TrySetException(operation.Error); operation.MarkErrorAsHandled(); } else if (operation.IsCanceled) { tcs.TrySetCanceled(); } else { tcs.TrySetResult(operation); } }; return tcs.Task; } }

To give some context on the next part, here’s the UI I’m using. It’s just a few DataGrids bound to the EntitySets on my DomainContext. I’ve also included a BusyIndicator to spin while I’m loading everything.

  <UserControl >

    <UserControl.Resources>
      <web:SampleDomainContext x:Key="sampleDomainContext" />
    </UserControl.Resources>
    
    <Grid x:Name="LayoutRoot" Background="White">
      <toolkit:BusyIndicator Name="busyIndicator">
        <StackPanel>

          <TextBlock Text="Reds" Margin="2" FontWeight="Bold"/>

          <sdk:DataGrid Name="redsDataGrid" IsReadOnly="True"
            ItemsSource="{Binding SampleRedEntities,
Source={StaticResource sampleDomainContext}}"/> <TextBlock Text="Greens" Margin="2" FontWeight="Bold"/> <sdk:DataGrid Name="greensDataGrid" IsReadOnly="True" ItemsSource="{Binding SampleGreenEntities,
Source={StaticResource sampleDomainContext}}"/> <TextBlock Text="Blues" Margin="2" FontWeight="Bold"/> <sdk:DataGrid Name="bluesDataGrid" IsReadOnly="True" ItemsSource="{Binding SampleBlueEntities,
Source={StaticResource sampleDomainContext}}"/> </StackPanel> </toolkit:BusyIndicator> </Grid> </UserControl>

Finally, here’s where I get to do all the neat stuff.

  public partial class MainPage : UserControl
  {
    private readonly SampleDomainContext _context;

    public MainPage()
    {
      InitializeComponent(); ;

      this._context =
(SampleDomainContext)this.Resources["sampleDomainContext"]; this.InitializeData(); } private async void InitializeData() { this.busyIndicator.IsBusy = true; // Serial await this._context.Load(
this._context.GetRedEntitiesQuery()).AsTask(); await this._context.Load(
this._context.GetGreenEntitiesQuery()).AsTask(); await this._context.Load(
this._context.GetBlueEntitiesQuery()).AsTask(); // Parallel await TaskEx.WhenAll( this._context.Load(this._context.GetRedEntitiesQuery()).AsTask(), this._context.Load(this._context.GetGreenEntitiesQuery()).AsTask(), this._context.Load(this._context.GetBlueEntitiesQuery()).AsTask() ); this.busyIndicator.IsBusy = false; } }

I’ve made the InitializeData method async so it starts to run in the constructor, but finishes well after. As you can see, I set the busy indicator to busy before loading data and then ,when all the data is successfully loaded, I set it to idle again. Also, I’ve shown two approaches for invoking multiple queries. First, I show how Loads can be invoked serially such that each is only started after the previous one completes. Next, I show how all the Loads can be run in parallel. The WhenAll method will ensure all the loads have completed successfully before I set my busy indicator to idle.

This is just a small sample of the things you can do with the Async CTP, but I’m already a big fan. Hopefully you find this helpful, and please let me know if you have any questions.

Comments (15)

  1. Matt Hudson says:

    Nice work!  Easy to understand, implemented it in just a few minutes.

  2. lee says:

    I used you extension method, to wait for all the data for the comboboxes is loaded.

    leeontech.wordpress.com/…/ria-services-and-combobox-lookups-with-async-ctp

  3. kylemc says:

    Awesome. Glad it helps.

  4. Peter Wone says:

    I don't understand… what does this give you that you can't already do with threads? Implicit use of pool threads?

  5. kylemc says:

    It's not really about threading (it can be, but everything running here is UI thread affinitized). It's about the async pattern. The patterns above are concise and robust in ways that are difficult to achieve now.

    For instance, in the serial case we're using three await statements. To achieve the same behavior with the current bits you would have to call the first load, wait for the callback, call the second load, wait for a callback, call the third load, wait for a callback, and then do something.

    In the parallel case we're running three async calls at the same time and waiting until they're all complete before doing something. Again, it's a difficult (or at least verbose) task with the current bits.

  6. grad says:

    Good article! would you please attach silverlight solution?

  7. kylemc says:

    There's not a whole lot more to it. Just create a new Silverlight Application, Add a DomainService, and modify the MainPage. Also, make sure both your projects reference the appropriate version of the AsyncCtpLibrary shipped with the CTP.

  8. Anne Sandjax says:

    Oren Beeri post a little bit simplified version with source code here :

    zormim.wordpress.com/…/using-the-new-async-framework-with-ria

  9. Zia says:

    I couldn't understand a small part of it. How do you implement this method?

    private async Task<RGB[]> GetEntitiesAsync() {…}

  10. kylemc says:

    @Zia

    I'd recommend taking a look at this link (msdn.microsoft.com/…/async.aspx) and going through some of the walkthroughs. The purpose of the API above was just to show how to consume an API that implemented the async pattern from a DomainService.

  11. Paul says:

    This works great went return entities or collection of entities. But now I have a method in the server the returns a string and it does not work. I get en error "I tried that and now I get

    Cannot implicitly convert type 'System.ServiceModel.DomainServices.Client.InvokeOperation<string>' to 'string'

    Any thoughts?

  12. kylemc says:

    @Paul

    The type you get back is Task<InvokeOperation<T>>. You can get the string using task.Result.Value.

  13. LarsM says:

    Thanks Kyle, excellent article.

    This all worked like a charm in my own project, until I upgraded my project to Silverlight 5 using all the latest bits from the recent SL5 release, and the AsyncCtpLibrary_Silverlight5.dll from the CTP3 package.

    It all compiled and ran after the upgrade, but no data was returned. Code was unchanged.

    When trying to revert to Silverlight 4, the compiler no longer recognizes await and async ….

    Anyone tried this successfully?

  14. kylemc says:

    @Lars

    Can you put together something from scratch using SL5 and Async? I'd assume so and that something went wrong in the upgrade. Thanks goodness for source control (hopefully :S). I'd be interested to know if you couldn't.

  15. Edward says:

    Exactly what I needed !