Asynchronous Services |
The openstack.net SDK is migrating to an asynchronous service model using the Task-based Asynchronous Pattern (TAP) for ongoing feature support. This page contains information about several aspects of the asynchronous interfaces which could result in some confusion during development.
Users unfamiliar with asynchronous programming may find the following introduction particularly valuable.
Support for asynchronous interfaces in this library takes advantage of two recent advancements in asynchronous programming support in .NET. First, the Task Parallel Library added base library support for creating asynchronous tasks. Second, language improvements allow for these tasks to be naturally and efficiently used within code. Depending on the environment used by your project, you may need to take additional steps in order to use these features.
The Task Parallel Library is used extensively by the implementation of this SDK. The library was originally added as part of .NET 4, users still working with .NET 3.5 make use of the Task Parallel Library for .NET 3.5 package using NuGet. This package is automatically installed by NuGet when the SDK package is added to a project targeting .NET 3.5.
Language support varies by language. The following table shows the language features available for several language, along with special considerations for use.
Language | Keywords | Visual Studio | .NET Framework |
---|---|---|---|
C# | async/await | Supported starting in Visual Studio 2012 Can compile in Visual Studio 2010 if UseHostCompilerIfAvailable is set to false (see below) | Supported in .NET 4.5+ |
Visual Basic | Async/Await | Supported starting in Visual Studio 2012 Can compile in Visual Studio 2010 if UseHostCompilerIfAvailable is set to false (see below) | Supported in .NET 4.5+ |
Visual C++ | Not available | Not available | Not available |
F# | async/let! | Visual Studio 2010+ | Supported in .NET 4+ |
To compile C# or Visual Basic projects which use the newer language support for asynchronous programming in Visual Studio 2010, the in-process compiler must be disabled. This is performed by manually editing the project file to include the following as the last element of the first <PropertyGroup> section in the project file.
<UseHostCompilerIfAvailable>false</UseHostCompilerIfAvailable>
Important |
---|
While the UseHostCompilerIfAvailable setting allows Visual Studio 2010 to compile C# and Visual Basic projects using async/await, the editor itself does not recognize these keywords. As a result, some functionality including but not limited to IntelliSense may not function if this option is used. |
Asynchronous methods are capable of throwing exceptions before creating a Task or during the asynchronous execution of the task itself. The documentation for asynchronous methods does not distinguish between these two cases, allowing for any of the specified exceptions to be thrown in either manner.
Important |
---|
This documentation uses the term asynchronous method to refer to any method with a return type of Task or TaskTResult. Languages with built-in support for asynchronous programming have their own related terminology which may differ in meaning. |
Exceptions thrown prior to the creation of the Task object representing the asynchronous operation must be caught directly by the calling code. For example, if the code throws an ArgumentNullException in this manner, the calling code would need to contain an exception handler for ArgumentNullException or ArgumentException to handle the exception.
Exceptions thrown during the asynchronous execution of the task are wrapped in an AggregateException object and returned by the Exception property. Exceptions thrown in this manner must be handled either by a task continuation that checks the Exception property, or by calling Wait or checking the Result property within an exception handling block that includes a handler for AggregateException.
This library additionally ensures that exceptions thrown by asynchronous operations are not wrapped in multiple layers of AggregateException. In other words, an ArgumentException thrown during the asynchronous execution of a task will result in the Exception property returning an AggregateException, and that exception will not contain an nested instances of AggregateException in the InnerExceptions collection. In most cases, the AggregateException wraps exactly one inner exception, which is the original ArgumentException. This guarantee simplifies the use of the API is languages that support async/await, since those operators automatically unwrap the first layer of AggregateException.
try { Task myTask = SomeOperationAsync(); myTask.Wait(); } catch (AggregateException wrapperEx) { ArgumentException ex = wrapperEx.InnerException as ArgumentException; if (ex == null) throw; // ex was thrown during the asynchronous portion of SomeOperationAsync. This is always the case if // SomeOperationAsync is an async function (§10.15 - C# Language Specification Version 5.0). }
Applications implementing specialized handling for exception which occur during asynchronous calls have multiple options available for consistent handling. The simplest solution, when available, involves using async/await. These operators automatically unwrap the first exception instance in the InnerExceptions collection of an AggregateException, resulting in behavior that appears to calling code as though the exception was directly thrown by the invoked method. The second method involves treating the original call as a continuation of another task, ensuring that all exceptions are presented as an AggregateException to the exception handling code. The following code shows the application of this strategy to an existing asynchronous call. Note that the CompletedTask class and Then extension method are part of the Rackspace Threading Library separately from this SDK.
// original asynchronous method invocation Task task1 = SomeOperationAsync(); // method invocation treated as a continuation Task task2 = task1.ContinueWith(_ => SomeOperationAsync());
Code using the continuation strategy for consistent error handling may benefit from the use of the Catch methods, which are also part of the Rackspace Threading Library. This extension method behaves in a manner similar to await, automatically unwrapping the first exception instance in the InnerExceptions collection of an AggregateException before invoking the continuation function which handles the exception.
Caution |
---|
All synchronous extension methods provided by this library are obsolete, and will be removed from a future release. |
The namespace net.openstack.Core.Synchronous contains extension methods that allow methods in an asynchronous service interface to be invoked synchronously. These extension methods are not recommended for use in new development, but are provided as a compatibility aid for projects where external restrictions preclude the direct use of the asynchronous APIs. These extension methods perform the following functions:
Invoke the asynchronous method, wait for the resulting Task to complete, and (where applicable) return the task result.
If an exception is thrown during the asynchronous execution of the method and wrapped in an AggregateException, the extension method unwraps the inner exception and throws it directly, just as would occur if the underlying method were executed synchronously.
The extensions for synchronous API calls do not expose all features of the underlying asynchronous API. In particular, the following limitations apply.
For asynchronous methods taking an AsyncCompletionOption parameter to control the behavior of the task created for asynchronous server-side operations, the synchronous extension always passes RequestSubmitted for the argument.
The synchronous extensions always pass None for the CancellationToken argument, and do not support asynchronous cancellation of the call.
The synchronous extensions do not support progress callbacks, and pass null to APIs with an IProgress parameter.