Extending the OData Async Extensions to DataServiceCollection Methods

UPDATED 8/5/2013: Turns out that I didn't have my DataServiceCollection<T> methods defined correctly as extension methods (forgot the this keyword on the bindingCollection parameter).

As I announced in a previous post, there is an OData client library that supports Windows Store app development. This is a the good news, but folks who have tried to use it in their Windows Store apps are quick to note that the library’s asynchronous APIs don’t support the nice new await/async behaviors in .NET Framework 4.5. Our friend Phani Raju provided a bit of help here in his post Async extension methods for OData Windows 8 client library. In this post, Phani provides us with some nice awaitable wrappers around the Begin/End methods in the OData client APIs. This makes is easy to add these extension methods into your project and use the await pattern, like this:

         var results = await ODataAsyncExtensions
            .ExecuteAsync<AnswerLink>(_context.AnswerLinks);

I used var to define the variable…but when you code this, you will notice that this awaitable method (along with the other Execute and Load methods) actually returns an IEnumerable<T>, which is not the ideal collection for data binding, especially two-way binding. OData clients already have an ideal binding collection in DataServiceCollection<T>, but Phani didn’t provide for us the nice awaitable wrapper for the load methods on this collection.

Fortunately, I came across the article  Convert Events to Async methods posted by Mandelbug, which showed how to convert the old style event/delegate based async operation into an awaitable Task-based one. As such, an awaitable version of the LoadAsync(DataServiceQuery) method looks like this:

         public static async Task<LoadCompletedEventArgs> 
            LoadAsync<T>(this DataServiceCollection<T> bindingCollection, 
            DataServiceQuery<T> query)
        {            
            var tcs = new TaskCompletionSource<LoadCompletedEventArgs>();

            EventHandler<LoadCompletedEventArgs> handler = 
                delegate(object o, LoadCompletedEventArgs e)
            {
                if (e.Error != null)
                {
                    tcs.TrySetException(e.Error);
                }
                else if (e.Cancelled)
                {
                    tcs.TrySetCanceled();
                }
                else
                {
                    tcs.TrySetResult(e);
                }
            };

            bindingCollection.LoadCompleted += handler;
            bindingCollection.LoadAsync(query);

            LoadCompletedEventArgs eventArgs = await tcs.Task;
            bindingCollection.LoadCompleted -= handler;

            return eventArgs;
        }

I have attached the entire ODataAsyncExtensions class code page to this post, which includes Phani’s methods plus my new LoadAsync ones. As always, this code is provided to you “as is," I ‘m not going to support it, so use it at your own risk….but please let me know if you find any bugs.

 

Cheers,

Glenn Gailey

ODataAsyncExtensions.cs

Comments

  • Anonymous
    November 24, 2013
    Hi, i have a question. Can you show a example of using your method LoadAsync?

  • Anonymous
    November 24, 2013
    Hey Goran, Here's an example of how to load the customers feed from Northwind:  // Create a LINQ query that returns customers with related orders.
     var customerQuery = from cust in _context.Customers
                            where cust.Country == customerCountry
                            select cust;
       
     // Create a new collection for binding.
     var _customers = new DataServiceCollection<Customer>(context);
     
        try
        {
            // Load products.
            await LoadProducts();         // Execute the query and get the first page of results.
            await _customers.LoadAsync<Customer>(customerQuery);         while (_customers.Continuation != null)
            {
                // Load all remaining pages of the response.
                await this.Customers.LoadAsync<Customer>(_customers.Continuation.NextLinkUri);
            }
        }
        catch (DataServiceQueryException ex)
        {
            this.Message = "The query could not be completed:n" + ex.ToString();
        } Note that this example calls LoadAsync() once to load the first page of data, and then it continues to call LoadAsync if there are additional pages in the response. Cheers, Glenn.

  • Anonymous
    November 25, 2013
    Works fantastically. Thanks a lot on this tip ;). Save me a lot of time