Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Fix: Calling WCF Methods with Parameters Synchronously on Windows Phone 7

In my previous post, Tidying up Windows Phone 7 + WCF Synchronous Programming Model I proposed a base class called SyncClientBase which would make it much easier to call WCF services from a background thread in a synchronous fashion. Unfortunately, whilst this works well for methods that don’t take any arguments, it completely fails when trying to call methods which take parameters. The first step in the Invoke method was to pull apart the Expression parameter in order to determine the async method that was to be invoked and the corresponding event that would be raised.  ie:

    public TResult Invoke<TResult>(Expression<Action> asyncMethod) 
                          where TResult : AsyncCompletedEventArgs {
        var memberExpression = asyncMethod.Body as MethodCallExpression;
        var method = memberExpression.Method;
        var eventName = method.Name.Replace("Async","Completed");
        var evt = Proxy.GetType().GetEvent(eventName);

 

This works, however, when it comes to actually invoking the async method I was passing in an empty object array – clearly not correct when the method takes parameters. On the desktop we could pull the Expression apart even more and actually pull out the arguments that were supplied in the expression tree. However, after playing around a bit I couldn’t work out how to do this without the .NET CF raising a FieldAccessException or two.

I decided to rework the Invoke method a little: Instead of accepting an Action Expression, it now takes a Func<Delegate> Expression (ie a function that returns the method we want to invoke). It also accepts an array of objects which will be the parameters to the async method to be invoked. The full Invoke method looks like:

public TResult Invoke<TResult>(Expression<Func<Delegate>> asyncMethod, params object[] args) where TResult : AsyncCompletedEventArgs
{     var body = asyncMethod.Body as UnaryExpression;     var operand = body.Operand as MethodCallExpression;     var arg = operand.Arguments[2] as ConstantExpression;     var method = arg.Value as MethodInfo;     var eventName = method.Name.Replace("Async", "Completed");     var evt = Proxy.GetType().GetEvent(eventName);     lock (downloadLock)     {         TResult data = default(TResult);         EventHandler<TResult> handler = (s, e) =>         {             data = e;             downloadWait.Set();         };         evt.AddEventHandler(Proxy, handler);         method.Invoke(Proxy, args);         downloadWait.WaitOne();         evt.RemoveEventHandler(Proxy, handler);         if (data.Error != null)         {             throw data.Error;         }         return data;     }
}
 

The proxy class structure still looks very similar. The only difference is that instead of a method call eg Proxy.Step1Async() we’re passing a delegate eg (Action)Proxy.Step1Async. For methods that require a parameter (eg Step3) the parameters are passed in as additional parameters to the Invoke method.

public class Service1Proxy : SyncClientBase<Services.Service1Client, ServiceTester.Services.IService1>, DemoService.IService1 {     public int Step1() {
        return base.Invoke<Step1CompletedEventArgs>(() => (Action)Proxy.Step1Async).Result;
    }
 
    public int Step2() {
        return base.Invoke<Step2CompletedEventArgs>(() => (Action)Proxy.Step2Async).Result;
    }
 
    public int Step3(int xyz) {
        return base.Invoke<Step3CompletedEventArgs>(() => (Action<int>)Proxy.Step3Async,xyz).Result;
    }
}
 

The only issue with this structure is that there is not validation that the correct number and type of parameters are passed into the Invoke method. Other than that, it’s still quite an elegant wrapper.

Comments are closed