Pages

Monday, July 6, 2015

Asynchronous Task Containers


A few years ago I posted about using Action delegates to create logical containers to encapsulate functionality. This technique is a good example of DRY programming, and it is very useful in scenarios where you are performing similar operations with different parameters such as calling a database or service.

Traditionally, .Net code has been very synchronous, and only since the introduction of the async and await keywords has there been a noticeable shift to asynchronous programming. I've had to update my pattern using Action delegates to create logical containers to accommodate asynchronous operations.

In a synchronous scenario, to create a container to encapsulate functionality, create a method that accepts an Action delegate of the Type that you want to perform an operation on:

public void ExecuteServiceCall(Action<IMobileServiceClient> serviceCall)
{
    using (MobileServiceClient service = new MobileServiceClient(ServiceUrl))
    {
        try
        {
            serviceCall.Invoke(service);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }
}

You would think that updating this method to be asynchronous would be as simple as adding the async and await keywords. But as you can see in the following image, the compiler does not like the fact that Invoking the delegate returns void.


So, how do you get around this issue?

Instead of using an Action delegate, use a Function delegate. The Function delegate will allow you to pass in an operation on a Type along with a Task parameter that can be used to notify the framework of the delegates completion.

public async void ExecuteServiceCall(Func<IMobileServiceClient, System.Threading.Tasks.Task> serviceCall)
{
    using (MobileServiceClient service = new MobileServiceClient(ServiceUrl))
    {
        try
        {
            await serviceCall(service);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }
}

This seems simple enough, but if you try to call this method, you will still encounter a problem. You can see in the following image that the compiler still needs a Task to be returned in order to properly utilize the async and await infrastructure.


Once again, this is an easy fix. If you update the method to return a Type of Task, and then return the instantiated Type from inside of the delegate, you'll find that the compiler will be happy, and that your asynchronous calls will work properly.

public async System.Threading.Tasks.Task<IMobileServiceClient> ExecuteServiceCall(Func<IMobileServiceClient, System.Threading.Tasks.Task> serviceCall)
{
    using (MobileServiceClient service = new MobileServiceClient(Ir360ServiceUrl))
    {
        try
        {
            await serviceCall(service);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }

        return service;
    }
}

Using this updated technique will allow you to stick to DRY programming principles and help you create cleaner, more efficient code!

I hope this helps!

No comments:

Post a Comment