Wednesday, 23 February 2011

Generate RX Methods For A WCF Client Using T4

This is a Text Template (T4) to generate reactive extensions (RX) for .Net extension methods for WCF service clients.

Reactive extensions for .Net is a library that simplifies coordination of asynchronous methods in .Net amongst other things. I’ll assume that if you’re reading this post you already know how to use RX, if not you could have a look at the beginners page.

I have my contrived WCF service that I have hosted in a console app and the service contract looks like this:

[ServiceContract]
[DataContractFormat]
public interface IDemoService
{
    [OperationContract]
    int MethodWithReturnValue(string arg1, int arg2);
 
    [OperationContract]
    void MethodWithNoReturnValue(string sdjsdkdskjksdj);
 
    [OperationContract]
    void NoArgsNoReturn();
 
    void NotAMethod();
}


I then turn to my client application and add a WCF service reference, and make sure I’ve checked the “Generate asynchronous operations” option.



image



To use the RX extensions, download the appropriate version and reference System.Reactive.dll in the client project. Creating the RX WCF extensions should be as simple as adding the T4 template to the client project. There is also a vb.net version in the zip file.



image



The T4 template generates an extension class for each service contract interface that it finds, with IObservable<T> methods for each asynchronous service operation.






public static IObservable<int> MethodWithReturnValueAsObservable(this ServiceReference2.IDemoService service, String arg1, Int32 arg2)
{
    return Observable
                .Start(()=> Observable.FromAsyncPattern<string, int, int>(service.BeginMethodWithReturnValue, service.EndMethodWithReturnValue)(arg1, arg2))
                .SelectMany(p=>p);
}


Observable.FromAsyncPattern is a method to turn a BeginXyz/EndXyz asynchronous pattern into a function that returns an observable collection. When you invoke the function and subscribe to the resulting Observable<T>, you can be notified when the result returns or combine it with other results. I have wrapped that method in a lambda expression so that it only gets invoked when the client subscribes to the Observable<T>.






()=> Observable.FromAsyncPattern<string, int, int>(service.BeginMethodWithReturnValue, service.EndMethodWithReturnValue)(arg1, arg2)


After that using the SelectMany(…) to flatten the IObservable<IObservable<T>> returned by the Start(…) method into an IObservable<T>.



Subscribing to the generated client looks something like this:






var client = new ServiceReference2.DemoServiceClient();
IObservable<int> observable = client.MethodWithReturnValueAsObservable("test", 12);
observable.Subscribe(
    next => MessageBox.Show(next.ToString()), 
    error => MessageBox.Show(error.Message), 
    () => MessageBox.Show("Complete"));


So in summary:





  1. Create your WCF service.

  2. Add a WCF service reference to the client project with asynchronous operations.

  3. Add a reference to Reactive eXtensions to the client project.

  4. Drop in the T4 template to generate the IObservable extension methods.

[UPDATE 1.0.0.1] Fixed templates for arrays and included correct T4 template for VB.net
[UPDATE 1.0.0.2] Added templates for RX 1.0.10621


Download T4 templates

3 comments:

  1. I was hoping you would solve the problem that FromAsyncPattern is only defined for up to two method parameters (on 3.5SP1 anyhow).

    ReplyDelete
  2. James: http://nuget.org/List/Packages/Rx-Main/1.0.10621 is the current recommended version as I'm writing this, does it have the same limitation on 3.5? I notice that the original WP7 is also limited to 2 args.

    ReplyDelete
  3. Hi
    Very interesting , but I have a slight problem.
    I've run the T4 but it doesn't seem to generate the correct method.
    My WCF method in the service takes one input parameter of type int, but the generated code doesn't create this param.

    ReplyDelete