Thursday, June 27, 2013

APM wrapper for synchronous methods using Task Library and Roslyn

 

Let me begin by saying this is a very very bad idea. For reasons, read this.

I use two techniques-

  1. Call Func<T> using TaskCompletionSource<TResult> as shown here
  2. Using Tasks to implement APM pattern as shown here

Let’s say you have method with the following signature

int MyLegacyMethod(ref myType param1, string param2, int param3, out param4);


Creating an APM wrapper for a method manually is a x step process-



  1. A one time step – Add an extension method ToApm as shown in the second technique above to implement APM pattern using Tasks.
  2. Write a method MyLegacyMethodAsync as below
    public Task<Tuple<int/*return type*/, myType /*ref param*/, char[] /*out param*/>> MyLegacyMethodAsync(myType param1, string param2, int param3)
    {
    myType param11 = param1; /* ref param for closure */
    char[] param4; /* out param for closure */
    var tcs = new TaskCompletionSource<Tuple<int, myType, char[]>>();

    ThreadPool.QueueUserWorkItem(_ =>
    {
    try
    {

    int retVal = MyLegacyMethod(ref param11 /* local var instead of ref param */, param2, param3, out param4);
    var result = new Tuple<int, myType, char[]>>(retVal, param11, param4);
    tcs.SetResult(result);
    }
    catch (Exception exc)
    {
    tcs.SetException(exc);
    }
    });
    return tcs.Task;
    }

  3. Write a BeginMyLegacyMethod as below
            public IAsyncResult BeginMyLegacyMethod(myType param1, string param2, int param3, AsyncCallback callback, object state)
    {
    return MyLegacyMethodAsync(param1, param2, param3).AsApm(callback, state);
    }


  4. Write a EndMyLegacyMethod as below
            public int EndMyLegacyMethod (IAsyncResult asyncResult, out myType param1, out char[] param4)
    {
    var result = ((Task<Tuple<int, myType, char[]>>) asyncResult).Result;

    param1 = result.Item2;
    param4 = result.Item3;

    return result.Item1;
    }

Now we are able to use our existing APM infrastructure to make APM calls to synchronous methods.


In my scenario, I am converting classes exposed over webservices to direct project reference. There are many classes with many methods and doing this manually would be error prone and mind numbing. So I used Roslyn library and wrote a simple class that



  1. reads in a file

  2. select methods marked with [WebMethod] attributes

  3. Generates the above 3 methods for each such method

  4. Writes them to a file

I have posted this on GitHub gists so that it may help someone save some time.



Happy Coding!