Showing an improvement on the pattern from the previous article

Working slightly smarter with WCF

Last time we showed how much dreary grunt work we have to go through just to make a minor change in a client-server distributed application, and postulated that there must be a smarter way.

Fortunately for me, I'm lucky enough to be working with Mark who's about as smart as they come. We discussed this at some length and we (well, mostly he) came up with something of an idea.

The cat-skinning method I'm going to show you has two basics building blocks:

  1. Placing the service interfaces and a single domain model in a common project, shared by the client and the server massively simplifies the whole process, and
  2. Dynamically building the proxy objects at runtime removes some of the legwork (instead of statically regenerating them by adding/updating the service reference).

For the first point: I know there are those that would argue that the client-side and server-side models are fundamentally very different beasts, with different purposes. I would agree wholeheartedly with that, but the reality is that for the majority - probably 90% or more - of the code-base I'm working on, the purpose of both models is simply to transport data up and down the wire, and around the application. And for the remaining 10%? well that's what partial class are there for...

So let's assume that point #1 holds up and we can move on to the real reason for writing this post - building a proxy dynamically to call into a WCF service from a distributed client...

First up, consider how Visual Studio builds a WCF service application for you through the 'File-New' process - you get an interface to the service, and the service itself. So our first job is to move that interface into the 'Common' area of the solution, so the client can 'see' the interfaces on the service, and generate proxy classes against them. I realise that we could simply add a reference to the server into the client, but by shifting the service interfaces and the model into a whole separate project, and not adding the server reference in the client, we can clearly delineate what the client developer can and cannot 'see'.

Now we need to configure the client with all the WCF stuff - the bindings and endpoints and whatever. The easiest way to do this is to simply (as a one-time activity) add a service reference and let Visual Studio do its thing.

alt text

We're not going to use any of the generated code, though, so you can delete the ServiceReference itself - we're just interested in generating the app.config.

One minor change in the config - the service interface will be pointing to the service interface coming out of the Service Reference, and we need to point it to the Common library...

change

        <endpoint 
            contract="ServiceReference1.ICustomerService">
        </endpoint>

to

        <endpoint 
            contract="TestApp.Common.ICustomerService">
        </endpoint>

Where TestApp.Common is the name of your common project.

Now, since we have removed the static proxy objects, we will need to generate one dynamically as we call the service methods. The dynamic proxy generation is made incredibly trivial with Castle's DynamicProxy code. I'm not going to explain the ins and outs of how to do this, since there is heaps of good material available on the web. This is a fairly good place to start if you're interested...

So we need to create a proxy class on the fly, whose responsibility is to hook up to WCF and send requests up and down the wire... sounds like a job for the Factory pattern, eh?

public static class ProxyFactory<T> where T : class
{
    public static T Create()
    {
        var proxyGenerator = new ProxyGenerator();

        var interceptor = new WcfInvocationInterceptor<T>(new WcfClient<T>());

        return proxyGenerator.CreateInterfaceProxyWithoutTarget<T>(interceptor);
    }
}

You can see that we are creating an instance of a class called WcfInvocationInterceptor<T>. This is the class that will intercept the proxy object creation and do the actual (WCF) work and pass it an instance of a WcfClient<T> - a wrapper for the System.ServiceModel.ClientBase<T> where T is the interface of the WCF Service being invoked...

Let's have a quick look at them both: Firstly, the interceptor...

public class WcfInvocationInterceptor<T> : IInterceptor where T : class
{
    private readonly WcfClient<T> _wcfClient;

    public WcfInvocationInterceptor(WcfClient<T> wcfClient)
    {
        _wcfClient= wcfClient;
    }

    #region Implementation of IInterceptor

    public void Intercept(IInvocation invocation)
    {
        invocation.ReturnValue = _wcfClient.Invoke(invocation.Method.Name, invocation.Arguments);
    }

    #endregion
}

Easy enough? We are benefiting massively from the super-powerful and super-simple DynamicProxy stuff from Castle. Here we use the Method and Arguments that are on the invocation object passed in, and pushing the result of our WCF call into the ReturnValue.

The WcfClient<T> is also incredibly trivial...

public class WcfClient<T> : System.ServiceModel.ClientBase<T> where T : class
{
    public object Invoke(string methodName, params object[] parameters)
    {
        var clientMethod = base.Channel.GetType().GetMethod(methodName);
        return clientMethod == null ? null : clientMethod.Invoke(base.Channel, parameters);
    }
}

We have a single method 'Invoke' that uses reflection to find the method on the System.ServiceModel.ClientBase<T>.Channel object and invoke that method. Could that be any simpler?

So what does that leave our client looking like? I'm really glad you asked - The client is beautiful in its simplicity...

    static void Main(string[] args)
    {
        var proxy = ClientFw.ProxyFactory<TestApp.Common.ICustomerService>.Create();

        foreach (var customer in proxy.GetAll())
        {
            Console.WriteLine(customer == null ? "null" : customer.ToString());
        }

        Console.WriteLine();
        Console.WriteLine(proxy.GetById("1"));

        Console.Read();
    }

The source code for this article is available on bitbucket or you can just directly download the source here. Download it, have a play and let me know what you think - I'd love your feedback on this...

wcf distributed client-server pattern
Posted by: Ian Randall
Last revised: 22 Jan, 2011 06:46 a.m.

Trackbacks

Comments

John Volkar
John Volkar
19 Jun, 2011 12:54 p.m.

Thank you go the article, it gave me the key to how to eliminate "Add Service Reference" and the resulting generated code from our application base. We were nearing the point that changing a service's operation signature during development was becoming prohibitively painful. We have about a dozen services, over 80 projects and updating the service references was horrible. (Not to mention the impossibility of getting everyone to remember to do it, do it consistently and not miss any places or otherwise make a mistake).

We were just starting to consider automatic updates by scripting svcutil into our build server, but yuck. This is a much better approach.

Some comments for others. Close/Dispose, Fault handling, and Callbacks. The example code above is short and easy to follow, but it is probably not a final production systems for anyone. For callbacks to work, you need DuplexClientBase and pass in the callback target around which an InstanceContext is to be constructed.

I'll be taking this approach and wiring it into a utility class for our application framework, I'll try to drop comments if I run into any problems, but I am very hopeful that this approach will work for us in production.

21 Jan, 2011 09:59 p.m.

A nice, clean approach - very clever.

Much smarter than my own idea for automating proxy generation (using svcutil) during the build process. I might need to evangelise a switch at work ...

Ian
Ian
21 Jan, 2011 11:35 p.m.

Thanks Bevan,

Hope it works out - let me know :)

Your Comments

Used for your gravatar. Not required. Will not be public.
Posting code? Indent it by four spaces to make it look nice. Learn more about Markdown.

Preview