65.9K
CodeProject is changing. Read more.
Home

Async OData

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (3 votes)

Sep 6, 2012

CPOL

1 min read

viewsIcon

21493

Show how to make OData code async friendly

Introduction

In this short tip, I explain how I made an OData sample using the async extension of .NET 4.5.

Using the Code

Today, I watched this video about using OData in a WinRT app.

To summarize, he (Jerry Nixon) adds a service reference to the Netflix OData API at http://odata.netflix.com/ and then proceeds to do something like:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    var _uri = new Uri("http://odata.netflix.com/Catalog/");
    var ctxt = new NetFlixService.NetflixCatalog(_uri);
    var data = new DataServiceCollection<NetFlixService.Title>(ctxt);
    var query = from t in ctxt.Titles
                where t.Name.Contains("Star Trek")
                select t;
    data.LoadCompleted += delegate 
    {
        this.DataContext = data;
    };
    data.LoadAsync(query);
}

Basically, he creates an OData service reference, runs a database query against it, and shows the results.

What I will show is how to improve this, how to use the new async / await keywords instead of the LoadCompleted delegate.

There seems to be no obvious way of doing that! Time to Google!

From the await (C# Reference) reference, it seems that await can (only) be applied to a method returning a Task, and a Task (from Googling) can be created from IAsyncResult.

So first, I started by creating a very simple and reusable IAsyncResult implementation class:

public class SimpleAsyncResult : IAsyncResult, IDisposable
{
    ManualResetEvent waitHandle = new ManualResetEvent(false);
    public void Finish()
    {
        IsCompleted = true;
        waitHandle.Set();
        waitHandle.Dispose();
    }
    public void Dispose() { waitHandle.Dispose(); }  

    public bool IsCompleted { get; private set; }
    public object AsyncState { get; set; }
    public bool CompletedSynchronously { get; set; }
    public WaitHandle AsyncWaitHandle { get { return waitHandle; } }
}

With that, I can easily create an extension method for my OData classes returning a Task:

public static class OData
{
    public static Task<DataServiceCollection<T>> AsyncQuery<T>(
           this DataServiceCollection<T> data, IQueryable<T> query = null)
    {
        var asyncr = new SimpleAsyncResult();
        Exception exResult = null;
        data.LoadCompleted += delegate(object sender, LoadCompletedEventArgs e)
        {
            exResult = e.Error;
            asyncr.Finish();
        };
        if (query == null)
            data.LoadAsync();
        else
            data.LoadAsync(query);
        return Task<DataServiceCollection<T>>.Factory.FromAsync(asyncr
            , r =>
            {
                if (exResult != null)
                    throw new AggregateException("Async call problem", exResult);
                return data;
            }
        );
    }
}

Remark: Here, I wrap the exception because I don’t want to lose the stack trace (with “throw exResult”).

And voila, I can update my NavigatedTo method to use the .NET4.5 async extension!

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    var _uri = new Uri("http://odata.netflix.com/Catalog/");
    var ctxt = new NetFlixService.NetflixCatalog(_uri);
    var data = new DataServiceCollection<NetFlixService.Title>(ctxt);

    var query = from t in ctxt.Titles
                where t.Name.Contains("Star Trek")
                select t;
    await data.AsyncQuery(query);
    this.DataContext = data; 
} 

Points of Interest

This is a nice way to explore some internals of the async extension in .NET4.5.

History

  • 1.0: First version