Click here to Skip to main content
Click here to Skip to main content
Go to top

Exposing asynchronous features to client code: Windows Phone 7

, 16 Jan 2011
Rate this:
Please Sign up or sign in to vote.
How to use types implementing the IAsyncResult interface on Windows Phone 7.

Introduction

Today, line of Internet and multimedia-enabled SmartPhones have become popular. Fortunately, if you are a .NET developer, you can use your existing skills and language and target some of the most popular mobile Operating Systems.

Windows Phone 7 is a mobile Operating System developed by Microsoft, and is the successor to their Windows Mobile platform.

Background

You can expose asynchronous features to client code using one of the following .NET Framework Asynchronous Programming models:

  • IAsyncResult The CLR's Asynchronous Programming Model (APM)
  • Event-based Asynchronous Pattern (EAP)

IAsyncResult has been around since .NET 1.0, and has been used in most BCL classes, while the main benefit of the EAP is that it integrates with the Visual Studio UI designers. You can learn how to properly implement the IAsyncResult interface (APM) from this excellent article on MSDN: Implementing the CLR Asynchronous Programming Model, by Jeffrey Richter.

In this post, I will show you how easy it is to use types implementing the IAsyncResult interface on Windows Phone 7. I will use the PowerThreading library[1] because it provides a similar (not to say an improved) implementation of the one described in the original MSDN article. I will explain how to use it and how this becomes easier using the AsyncEnumerator class.

The Problem

The code linked to above is used for the demo. This code does some I/O which causes a thread to block.

public interface IWebService
{
    IStockQuote FetchStockQuotes();
}

internal sealed class WebService : IWebService
{
    private readonly IStockQuote m_quotes;

    /// <summary>
    /// Initializes a new instance of the <see cref="WebService"/> class.
    /// </summary>
    /// <param name="quotes">The quotes.</param>
    public WebService(IStockQuote quotes)
    {
        m_quotes = quotes;
    }

    #region IWebService Members
    /// <summary>
    /// Gets the stock quotes.
    /// </summary>
    /// <returns></returns>
    public IStockQuote FetchStockQuotes()
    {
        Thread.Sleep(50); // Simulate time-consuming task.
        return m_quotes;
    }
    #endregion
}

Sync I/O Pauses the UI

The code below shows how the ExecuteWithSyncIO method is implemented. The application shows a MessageBox to the user that the UI will pause while the execution is in progress.

private void ExecuteWithSyncIO()
{
    for (Int32 n = 0; n < c_iterations; n++)
    {
        m_webService.GetStockQuotes();
    }
    SetStatus("Sync/IO completed.", StatusState.Ready);
}

Delegate's BeginInvoke Method is Not Supported

The code below shows how the ExecuteWithDelegateBeginInvoke method is implemented.

This method is here just for the demo, since it is not allowed to invoke a delegate asynchronously in the Compact Framework.

private void ExecuteWithDelegateBeginInvoke()
{
    Func<IStockQuote> stockQuoteDelegate = m_webService.GetStockQuotes;
    // NOTE: Calling delegates asynchronously is NOT supported in WP7.
    stockQuoteDelegate.BeginInvoke((ar) => {
        stockQuoteDelegate.EndInvoke(ar);        
    }, null);
}

The Solution

The Wintellect.Threading.AsyncProgModel.AsyncResult<TResult> class contains an implementation of the IAsyncResult interface. This type is generic and we can easily use it on the WebService class.

using System;
using System.Threading;
using Wintellect.Threading.AsyncProgModel;

public interface IWebService
{
    IStockQuote FetchStockQuotes();
}

internal sealed class WebService : IWebService
{
    private readonly IStockQuote m_quotes;

    /// <summary>
    /// Initializes a new instance of the <see cref="WebService"/> class.
    /// </summary>
    /// <param name="quotes">The quotes.</param>
    public WebService(IStockQuote quotes)
    {
        m_quotes = quotes;
    }

    // Asynchronous version of time-consuming method (Begin part).
    public IAsyncResult BeginGetStockQuotes(AsyncCallback callback, Object state)
    {
        // Create IAsyncResult Object identifying the 
        // asynchronous operation.
        AsyncResult<IStockQuote> ar = 
             new AsyncResult<IStockQuote>(callback, state);

        // Use a thread pool thread to perform the operation.
        ThreadPool.QueueUserWorkItem(GetStockQuotesHelper, ar);

        return ar; // Return the IAsyncResult to the caller.
    }

    // Asynchronous version of time-consuming method (End part).
    public IStockQuote EndGetStockQuotes(IAsyncResult asyncResult)
    {
        // We know that the IAsyncResult is really an 
        // AsyncResult<IStockQuote> object.
        AsyncResult<IStockQuote> ar = 
               (AsyncResult<IStockQuote>)asyncResult;

        // Wait for operation to complete, then return result or 
        // throw exception.
        return ar.EndInvoke();
    }

    private void GetStockQuotesHelper(Object state)
    {
        // We know that it's really an AsyncResult<IStockQuote> object.
        AsyncResult<IStockQuote> ar = (AsyncResult<IStockQuote>)state;
        try
        {
            // Perform the operation; if sucessful set the result.
            IStockQuote quotes = FetchStockQuotes();
            ar.SetAsCompleted(quotes, false);
        }
        catch (Exception e)
        {
            // If operation fails, set the exception.
            ar.SetAsCompleted(e, false);
        }
    }

    #region IWebService Members
    /// <summary>
    /// Gets the stock quotes.
    /// </summary>
    /// <returns></returns>
    public IStockQuote FetchStockQuotes()
    {
        Thread.Sleep(5); // Simulate time-consuming task.
        return m_quotes;
    }
    #endregion
}

Actually, the GetStockQuotesHelper method could be inlined. I try to avoid inlined delegates because you can easily access variables defined in the parent method body.

Let's look now at how the above can be used on Windows Phone 7.

IAsyncResult Interface

The code below shows how the ExecuteWithIAsyncResult method is implemented. The only problem is that, when using the IAsyncResult, you need to specify a method to be called when a corresponding asynchronous operation completes. This can result in using synchronization constructs to avoid race conditions. It also splits the flow of your code. You can inline the callback method using Anonymous Methods or Lambda Expressions as shown below, but if your logic is complicated, your code will not be beautiful.

private void ExecuteWithIAsyncResult()
{
    SetStatus("Working..", StatusState.Busy);
 
    for (Int32 n = 0; n < c_iterations; n++)
    {
        m_webService.BeginGetStockQuotes((ar) => {
            // Callback method inlined using Lamda Expressions.

            // NOTE: Code can become ugly here, specially if you need to do 
            // a lot of stuff that touch properties bounded with UI elements.
            if (Interlocked.Increment(ref m_numDone) == c_iterations)
            {
                Execute.OnUIThread(() => {
                    SetStatus("IAsyncResult APM completed.",
                        StatusState.Ready);
                });
            }
        }, null);
    }
}

AsyncEnumerator Class

The code below shows how the ExecuteWithAsyncEnumerator method is implemented. As you can see, this method makes your code looks like it's executing synchronously, but actually it executes asynchronously. You do not have to split your code in callback methods or inlined delegates. You do not need to marshal calls in the UI thread using the Dispatcher or the SynchronizationContext. All this stuff is handled by the AsyncEnumerator class.

private IEnumerator<Int32> ExecuteWithAsyncEnumerator(AsyncEnumerator ae)
{
    for (Int32 n = 0; n < c_iterations; n++)
    {
        m_webService.BeginGetStockQuotes(ae.End(), null);
    }
 
    // AsyncEnumerator captures the calling thread's SynchronizationContext.
    // Set the Wintellect.Threading.AsyncProgModel.SynchronizationContext to
    // null so that the callback continues on a ThreadPool thread.
    ae.SyncContext = null;
    yield return c_iterations;

    for (Int32 n = 0; n < c_iterations; n++)
    {
        m_webService.EndGetStockQuotes(ae.DequeueAsyncResult());
    }
 
    // AsyncEnumerator captures the synchronization context.
    SetStatus("AsyncEnumerator completed.", StatusState.Ready);
}

Points of Interest

While what I've discussed in this post applies to Mobile application development, the same principles can be applied to Rich Internet applications and Smart Clients. I have been using the AsyncEnumerator class for over two years, and I have to say that it changed the way I think about using the APM. In the end.. delivering responsive applications makes the end-users happy.

More samples can be found on my GitHub repository.

References

  1. AsyncEnumerator class resides in the PowerThreading library. It was written by Jeffrey Richter and can be obtained from the Wintellect website.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author


Comments and Discussions

 
GeneralMy vote of 5 PinmvpKanasz Robert21-Sep-12 1:39 
GeneralUpdates on Article's Source Code Location PinmemberNikos Baxevanis15-May-11 2:40 
GeneralRe: Updates on Article's Source Code Location PinmemberVlad POP27-Jun-11 8:17 
GeneralRe: Updates on Article's Source Code Location Pinmemberr.t.kreis27-Jun-11 22:09 
GeneralRe: Updates on Article's Source Code Location PinmemberVlad POP27-Jun-11 23:50 
GeneralRe: Updates on Article's Source Code Location PinmemberMember 792415628-Jun-11 21:02 
GeneralRe: Updates on Article's Source Code Location PinmemberVlad POP28-Jun-11 23:19 
GeneralMy vote of 5 Pinmemberlocu816-Apr-11 9:01 
GeneralRe: My vote of 5 PinmemberNikos Baxevanis15-May-11 2:38 
GeneralCool article! PinmemberBrianSBoyd21-Jan-11 19:33 
GeneralRe: Cool article! PinmemberNikos Baxevanis15-May-11 2:39 
GeneralMy vote of 5 PinmemberYusuf17-Jan-11 9:24 
GeneralRe: My vote of 5 PinmemberNikos Baxevanis15-May-11 2:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 16 Jan 2011
Article Copyright 2011 by Nikos Baxevanis
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid