Accessing Data Abstract Servers in an Async/Await Manner

The upcoming Visual Studio 2012 and C# 5.0 will bring a really cool feature – the async/await magic keyword. Utilizing the power of the System.Threading.Tasks.Task, they will make it much easier to create asynchronous applications. And this will lead us to the new shiny world of responsive and robust applications. I hope.

Both RemObjects SDK and Data Abstract provide asynchronous interfaces. Yet they are based on the old Begin/End asynchronous pattern. Although still valid, this pattern feels outdated and results in having to write more code. Just imagine – all the callbacks, the delegates for handling method call results, the UI thread marshalling issues and so on, and so on. Wouldn’t it be cool to be able to call Data Abstract servers using code like this:

dgClients.ItemsSource= await LoadDataAsync(linqRemoteDataAdapter.GetTable<Clients >().Select(x => x));

without these pesky callbacks and Dispatcher.Invoke calls like:

privatevoid selectClients(){var lQuery =(from x in linqRemoteDataAdapter.GetTable< Clients>()select x); IQueryable [] lQueries =new IQueryable []{ lQuery }; linqRemoteDataAdapter.BeginExecute( lQueries, delegate( IAsyncResult ar){ linqRemoteDataAdapter.EndExecute(ar); Dispatcher.BeginInvoke(new ProcessSelectClientsResultCb (ProcessSelectClientsResult), lQuery.ToList<Clients >());}, null);}   publicdelegatevoid ProcessSelectClientsResultCb (List <Clients > result);   publicvoid ProcessSelectClientsResult( List< Clients> result){this.dgClients.ItemsSource= result;}

If you are interested, read this article. We will create a Silverlight application using the old approach and then convert it to utilize the new async/await asynchronous calls pattern.

Please note that in this application I won’t use the MVVM approach, because I want to put the focus on the asynchronous service calls and to simplify the code used in the article.

Begin/End-based Application

Let’s create a Silverlight application that will access an MS SQL database containing this data table:

CREATETABLE[dbo].[Orders]([OrderId][INT]IDENTITY(1,1)NOTNULL,[OrderDetails][nvarchar](50)NOTNULL,CONSTRAINT[PK_Orders]PRIMARYKEY([OrderId]ASC))

Using the Data Abstract New Project Wizard (.NET), this can be done in literally less than 1 minute.



Now let’s add some code to actually load the data:

Change the MainPage.xaml layout file to:

x:Class="SilverlightApplication1.MainPage" xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" Loaded ="MainPage_Loaded" Width ="640"Height="480">Background="White">>Load Data>>Insert Data>>Apply Changes> x:Name ="dataList"AutoGenerateColumns="True">>>>

This gives us the following Sliverlight page design:



Note that the user credentials popup was cut from the MainPage.xaml file. We will use a hardcoded username and password, so there is no need for a LogOn UI.

Now let’s define actions for the buttons added to the MainPage. This is the full code of the MainPage.xaml.cs file:

usingSystem;usingSystem.Linq;usingSystem.Windows;usingSystem.Windows.Controls;usingRemObjects.DataAbstract.Linq;usingSilverlightApplication.SilverlightApplicationDataset;   namespace SilverlightApplication {publicpartialclass MainPage: UserControl {#region Private fieldsprivate DataModule fDataModule;private DABindingList<Orders> fOrdersList;#endregion   public MainPage(){this.InitializeComponent();   this.fDataModule=new DataModule ();}   privatevoid LogOn(){this.fDataModule.LogOn("a" , "a" , null);}   privatevoid MainPage_Loaded(object sender, RoutedEventArgs e){this.LogOn();}   privatevoid LoadData(object sender, RoutedEventArgs e){var lQuery =(from o inthis.fDataModule.DataAdapter.GetTable<Orders >()select o); IQueryable [] lQueries =new IQueryable []{ lQuery };   this.fDataModule.DataAdapter.BeginExecute( lQueries, (ar)=>{this.fDataModule.DataAdapter.EndExecute(ar);   this.fOrdersList= lQuery.ToDABindingList(); Dispatcher.BeginInvoke(()=>{this.dataList.ItemsSource=this.fOrdersList;});}, null);   }   privatevoid InsertData(object sender, RoutedEventArgs e){var lDummyData =new Orders(); lDummyData.OrderDetails="Object created at "+ DateTime .UtcNow.ToString();   this.fOrdersList.Add(lDummyData);}   privatevoid ApplyChanges(object sender, RoutedEventArgs e){this.fDataModule.DataAdapter.BeginApplyChanges((ar)=>{this.fDataModule.DataAdapter.EndApplyChanges(ar);}, null);}}}

Take a look at the LoadData and ApplyChanges methods code. It looks overcomplicated and contains one serious yet not always visible bug.

When data changes are applied on the server and the underlying data table contains auto inc or any other server-generated or server-calculated fields, Data Abstract sends the values of these fields back to the client where the source data object is updated with the refreshed values. In the code above, this update happens when the EndApplyChanges method is called. And this results in an exception, because the EndApplyChanges method updates one of the objects contained in the fOrdersList entities list. But this method is executed in a thread other than the main UI thread, while the fOrdersList is bound to the UI property this.dataList.ItemsSource. So the entire application fails with an “Invalid cross-thread access.” exception.

To avoid cross-thread access, the ApplyChanges code is even more of a burden:

privatevoid ApplyChanges(object sender, RoutedEventArgs e){this.fDataModule.DataAdapter.BeginApplyChanges((ar)=>{Dispatcher.BeginInvoke(()=>{this.fDataModule.DataAdapter.EndApplyChanges(ar);});}, null);}

Looks ugly, doesn’t it?

Now let’s move to the second part of this article and try to use the opportunities given by the new async/await features.

Async/Await-based Application

Surprisingly, Silverlight 5 in the Visual Studio 2012 RC doesn’t recognize the async/await keywords out of the box. You have to install the NuGet package Microsoft.CompilerServices.AsyncTargetingPack to be able to use this language feature. Thankfully, this operation is very straightforward:

  1. Right-click the Silverlight project in the Solution Explorer and select the “Manage NuGet Packages…” menu item.
  2. The NuGet main dialog will be shown. Search for the package Microsoft.CompilerServices.AsyncTargetingPack and install it.



Now let’s start to change the application. The first step is to change the ApplyChanges handling:

privatevoid ApplyChanges(object sender, RoutedEventArgs e){this.ApplyChangesAsync();}   async Task ApplyChangesAsync(){ await Task .Factory.FromAsync(this.fDataModule.DataAdapter.BeginApplyChanges, (ar)=>{ Dispatcher.BeginInvoke(()=>{this.fDataModule.DataAdapter.EndApplyChanges(ar);});}, null);}

Note how much simpler the call to the ApplyChanges method in the button click handler looks. And this method is executed asynchronously under the hood.
(That’s why I had to keep that ugly Dispatcher.BeginInvoke call. In a real world application, one should never expose objects acquired from the data access layer directly in the UI.)

The next stop is the data loading code. It should look like this:

async privatevoid LoadData(object sender, RoutedEventArgs e){this.dataList.ItemsSource= await LoadDataAsync<Orders>(this.fDataModule.DataAdapter.GetTable<Orders>().Select(o => o));}   async Task<DABindingList <T>> LoadDataAsync<T>(IQueryable <T> query)where T:class, new(){ Task lTaskRunner = Task.Factory.FromAsync<IQueryable []>(this.fDataModule.DataAdapter.BeginExecute, this.fDataModule.DataAdapter.EndExecute, new IQueryable []{ query }, null);   return await lTaskRunner.ContinueWith<DABindingList<T>>((task)=>{return query.ToDABindingList<T>();});}

At first glance it looks even more complicated than the previous version. However, note the following:

  1. The LoadDataAsync method now can be moved to the DataModule. This wasn’t possible in the previous code version because of the Dispatcher.BeginInvoke calls.
  2. The dispatcher is not called directly.
  3. The same LoadDataAsync method can be used to load any data table via DA LINQ.

These two changes hide the data access code complexity, allowing to access data in the new async/await manner.

Also take a closer look at the LoadDataAsync method code. The ContinueWith method call creates a chain of asynchronous tasks, so the second part of the data loading process (i.e. converting the retrieved data stream into a bindable list) is performed asynchronously as well. This is great because it’s no effort to synchronize these two asynchronous tasks, as this is done by the .NET framework itself now.

Calling service methods in the new manner is very simple as well. Check out this code where the LoginEx method is called:

async publicvoid LogOn(String userId, String password, WaitCallback callback){ RemObjects.DataAbstract.Server.IBaseLoginServiceAsync lAsyncService =(new RemObjects.DataAbstract.Server.BaseLoginServiceAsyncProxy(this.fMessage, this.fClientChannel, "LoginService"));   Boolean lLoginResult = await Task.Factory.FromAsync<String, Boolean>(lAsyncService.BeginLoginEx, lAsyncService.EndLoginEx, ConnectionString, null);}

This looks really easy, doesn’t it?

You should be aware that there is a limitation to the Task.Factory.FromAsync method – it only allows to convert methods to tasks that have no more than 3 arguments.

Our team has plans to implement a code gen that will generate an async/await-enabled interface files for RemObjects SDK services to make it even easier to access Data Abstract and RemObjects SDK servers in the new asynchronous way.

Summary

There is no black magic behind the new async/await asynchronous model. Despite the lack of built-in support, the new asynchronous model can be easily utilized by Data Abstract and RemObject SDK-enabled applications.

antonk

0