Click here to Skip to main content
12,755,975 members (33,958 online)
Click here to Skip to main content
Add your own
alternative version

Stats

11.4K views
217 downloads
16 bookmarked
Posted 31 May 2012

Orchestration of asynchronous communication

, 31 May 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
How to centralize and unify asynchronous execution of actions and functions.

Orchestrating the asynchronous communication

 

Introduction

 

Asynchronous communication is becoming more and more popular and widespread nowadays. It is mainly used to improve performance in multi-processor environments and to increase responsiveness of applications. At the latest with the introduction of Silverlight, asynchronous communication has become a must, because all web requests in that runtime happen asynchronously. This trend continues with Windows 8 on an even more fundamental level, as many system calls happen asynchronously there.

Implementing asynchronous communication asks for additional skills in developers, because they lose control over the sequence of operations to a certain degree, which in turn leads to distributed but still related pieces of source code, which contradicts some OO paradigms.

This article describes some simple means by which dealing with asynchronous communication can be greatly simplified. The associated library provides several tools with the following benefits:

  • Unification of the sequence of operations in 4 areas: Main, OnSuccess, OnError, OnFinally
  • Combination of the code of a sequence of operation
  • Type safety of return values and parameters through usage of generics
  • Automatic reuse of functions' return values
  • Simplification of error and exception handling
  • Centralizing the core of asynchronous communication in one place for easier analysis, logging etc.

Asynchronous Action


Asynchronous Action

 

To asynchronously execute an action, the library provides the class AsyncAction, which in turn offers various utility methods for starting it. The most elaborate signature is defined as follows:

 

public static void Start(
  Invocable main,
  Invocable onSuccess,
  InvocableAction1Base<Exception> onError,
  Invocable onFinally,
  AsyncContext context )

The parameters main, onSuccess, onError, onFinally represent the code blocks according to the above operation sequence diagram. In case Main is executed without error, the OnSuccess block is started. Otherwise the resulting Exception is given as a parameter to OnError. For the use of AsyncAction, a given Main action is mandatory. The AsyncContext encapsulates the execution context and is described further down.

In the following example, an OnSuccess action is used aside from the Main action:

// ----------------------------------------------------------------------
public void DeleteAllCustomersMinimal()
{
  AsyncAction.Start(
    new InvocableAction0( Services.DeleteAllCustomers ),
    new InvocableAction0( () => Customers.Clear() ) );
} // DeleteAllCustomersMinimal

Actions are available in various variants with up to 4 parameters: InvocableAction0InvocableAction4:


Asynchronous Action Classes

 

Given this inheritance hierarchy, calls to the respective actions can be customized individually as required. The following example demonstrates how to furnish the OnError action with an additional Customer parameter by using the InvocableAction2:

 

// ----------------------------------------------------------------------
public void DeleteCustomer( Customer customer )
{
  AsyncAction.Start(
    // main
    new InvocableAction1<int>( 
      Services.DeleteCustomer, customer.Id ),
    // on success
    new InvocableAction1<Customer>( 
      c => Customers.Remove( c ), customer ),
    // on error
    new InvocableAction2<Exception, Customer>( 
      ( e, c ) => ShowError( e + " - " + c ), customer ),
    // on finally
    new InvocableAction1<Customer>( 
      c => Debug.WriteLine( "DeleteCustomer: " + c ) ) );
} // DeleteCustomer

Asynchronous Function


Asynchronous Function

 

Asynchronous functions can be started through the different variants of the start method in the class AsyncFunction. The most elaborate signature with all execution parameters is defined as follows:

 

public static void Start(
  InvocableFunction<TResult> main,
  InvocableAction1Base<TResult> onSuccess,
  InvocableAction1Base<Exception> onError,
  Invocable onFinally,
  AsyncContext context )

The parameters main, onSuccess, onError, onFinally represent the code blocks according to the above operation sequence diagram. Contrary to the AsyncAction, the parameter Main is of type InvocableFunction, hence returning a result. In case Main is executed without error, the OnSuccess block is started. The return value of the Main function is given to the OnSuccess action as its first parameter. In case execution of the Main function results in an exception, it is passed as a parameter to the OnError action. Otherwise the resulting Exception is given as a parameter to OnError. For the use of AsyncFunction, a given Main function and a corresponding OnSuccess action are mandatory. The AsyncContext encapsulates the execution context and is described further down.

The following example demonstrates how the result of the Main Function is passed to the OnSuccess action:

// ----------------------------------------------------------------------
public void GetCustomerSalesVolumeMinimal( Customer customer )
{
  AsyncFunction<double>.Start(
    // main
    new InvocableFunction1<int, double>(
      Services.GetCustomerSalesVolume, customer.Id ),
    // on success
    new InvocableAction2<double, Customer>(
      ( sv, c ) => c.SalesVolume = sv, customer ) );
} // GetCustomerSalesVolumeMinimal

Suitable arguments for the Main parameter of AsyncFunction can be functions with up to 4 parameters InvocableFunction0InvocableFunction4:


Asynchronous Function Classes

 

Execution context

 

At the center of asynchronous communication is the class AsyncContext, which is used by AsyncAction and AsyncFunction. It governs the following aspects:


Dispatcher  

The Dispatcher which is used to actually run the asynchronous Action or Function. By default it will use the Dispatcher of the current thread.

Thread  

Given the property ShouldInvokeOnDispatcherThread, it is possible to decide whether the Dispatcher should be run in the current Thread or via a ThreadPool in separate Thread.

Invoke  

Implements the central operation sequence which actually executes the asynchronous action or function.

Error handling

For proper handling of server side errors in case of asynchronous web communication (Silverlight), further steps are required. It is necessary to encapsulate errors which occur on the server and transfer them (via WCF) to the client.

This article abstains from a description of fault handling because it is not strictly on its main topic and beyond its scope. There are however many examples of how to accomplish this, for example in Catch and Handle WCF Service Exceptions in Silverlight.

Examples of application

Asynchronous communication demo application

 

The bundled sample application for WPF, Silverlight and Windows Phone demonstrates how to load a list of customer information items asynchronously:

 

// ----------------------------------------------------------------------
private void LoadCustomers()
{
  AsyncFunction<CustomerCollection>.Start(
    // main
    new InvocableFunction0<CustomerCollection>( services.LoadCustomers ),
    // on success
    new InvocableAction1<CustomerCollection>( OnCustomersLoaded ),
    // on error
    new InvocableAction1<Exception>( OnCustomerLoadError ),
    // on finally
    new InvocableAction1<string>( OnFinished, "LoadCustomers" ) );
} // LoadCustomers

// ----------------------------------------------------------------------
private void OnCustomersLoaded( CustomerCollection customers )
{
  CustomerList.ItemsSource = customers;
  if ( customers != null && customers.Count > 0 )
  {
    CustomerList.SelectedIndex = 0;
  }
  AddToHistory( string.Format( "{0} customers loaded", customers != null ? customers.Count : 0 ) );
} // OnCustomersLoaded

// ----------------------------------------------------------------------
private void OnCustomerLoadError( Exception exception )
{
  AddToHistory( string.Format( "error: {0}", exception.Message ) );
} // OnCustomerLoadError

// ----------------------------------------------------------------------
private void OnFinished( string action )
{
  AddToHistory( string.Format( "'{0}' finished", action ) );
} // OnFinished

After the customers have been loaded, they are displayed in a list. Selecting a customer in that list will show its data in a detail section. Additionally, the sales volume of that customer is fetched asynchronously from an external service:

private void UpdateCustomerInfo( Customer customer )
{
  // static customer data
  CustomerId.Text = customer.Id.ToString( CultureInfo.InvariantCulture );
  CustomerFirstName.Text = customer.FirstName;
  CustomerLastName.Text = customer.LastName;
 
  // sales volume
  AsyncFunction<double>.Start(
    // main
    new InvocableFunction1<int, double>( services.GetCustomerSalesVolume, customer.Id ),
    // on success
    new InvocableAction2<double, Customer>(
      ( salesVolume, cust ) =>
      {
        cust.SalesVolume = salesVolume;
        CustomerSalesVoume.Text = salesVolume.ToString( "C" );
        AddToHistory( string.Format( "customer {0} sales volume: {1:C}", cust, salesVolume ) );
      },
      customer ),
    // on error
    new InvocableAction2<Exception, Customer>(
      ( e, cust ) =>
      {
        CustomerSalesVoume.Text = string.Empty;
        AddToHistory( string.Format( "error get sales volume: {0}", cust ) );
      },
      customer ),
    // on finally
    new InvocableAction1<string>(
      action => AddToHistory( string.Format( "'{0}' finished", action ) ),
      "GetCustomerSalesVolume" ) );
} // UpdateCustomerInfo

Acknowledgement

Special thanks to Leon Poyyayil for the design and initial implementation and his support and contribution in the development of this component.

History

  • 1st June, 2012 - v1.0.0.0
    • Initial public release

License

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

Share

About the Author

Jani Giannoudis
Software Developer (Senior)
Switzerland Switzerland
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionQuestion on similarities to the new TASK class Pin
xzz01955-Jun-12 14:05
memberxzz01955-Jun-12 14:05 
AnswerRe: Question on similarities to the new TASK class Pin
Jani Giannoudis5-Jun-12 21:14
mvpJani Giannoudis5-Jun-12 21:14 
GeneralRe: Question on similarities to the new TASK class Pin
xzz01957-Jun-12 19:22
memberxzz01957-Jun-12 19:22 
QuestionThis is a copy of Microsoft Reactive Extensions Pin
carldebilly1-Jun-12 6:03
membercarldebilly1-Jun-12 6:03 
QuestionRe: This is a copy of Microsoft Reactive Extensions Pin
Jani Giannoudis3-Jun-12 0:16
mvpJani Giannoudis3-Jun-12 0:16 
AnswerRe: This is a copy of Microsoft Reactive Extensions Pin
carldebilly3-Jun-12 4:36
membercarldebilly3-Jun-12 4:36 
QuestionArticle needs editing Pin
Phil J Pearson1-Jun-12 0:53
memberPhil J Pearson1-Jun-12 0:53 
AnswerRe: Article needs editing Pin
Jani Giannoudis1-Jun-12 2:05
mvpJani Giannoudis1-Jun-12 2:05 

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

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

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170217.1 | Last Updated 1 Jun 2012
Article Copyright 2012 by Jani Giannoudis
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid