Making Asynchronous WCF calls without SvcUtil

On the course of last few months, I’ve been working with Craig Neuwirt, on what I consider one of the coolest additions to Castle WCF Integration Facility.

Problem

As you probably know by default all WCF calls are synchronous – you make a request, under the cover WCF blocks your thread using a WaitHandle waiting for response, then it unblocks your threads making it look like a local call. This makes things simple for a programmer, who does not have to deal with synchronization, but it’s an overkill from scalability and performance perspective.

There are also one way calls, often mistakenly called ‘fire and forget’ which are still synchronous, but return control to your thread as soon as they get confirmation from the other end that message was received.

There are also actual asynchronous calls, but to take advantage of this you have to either use svcutil to generate your code, or build asynchronous versions of your contracts manually, which is tedious, forces you to remember quite a lot details about how asynchronous contracts should look like, and there’s no way compiler will tell you your sync and async contracts are out of sync (no pun intended).

Solution

Using trunk version of WCF Facility you can take advantage of a new mechanism that lets you perform asynchronous WCF calls having just the synchronous contract. Let me show you an example.

asyncwcf_solution

Contract project holds the service contract that both client and service share:

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    string MyOperation(string message);
}

Notice there’s no asynchronous version of the operation.

Service project contains only simple implementation of the contract plus console host.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MyService : IMyService
{
    public string MyOperation(string message)
    {
        Console.WriteLine("Called non one way operation... with " + message);
        Thread.Sleep(1000);
        Console.WriteLine("Done calculating");
        return message.ToUpperInvariant();
    }
}

It uses Thread.Sleep to simulate some lengthy operation, so that we can actually see that client does not wait for the end of the operation.

The service host uses WCF Facility to configure the service:

class Program
{
    static void Main(string[] args)
    {
        using (StartService())
        {
            Console.WriteLine("Running");
            Console.ReadKey(true);
        }
    }
 
    private static IWindsorContainer StartService()
    {
        return new WindsorContainer()
            .AddFacility<WcfFacility>()
            .Register(Component.For<MyService>().ActAs(
                        new DefaultServiceModel().AddEndpoints(
                            WcfEndpoint.ForContract<IMyService>()
                                .BoundTo(new NetTcpBinding())
                                .At("net.tcp://localhost/Service"))));
    }
}

Client project is even simpler:

class Program
{
    static void Main()
    {
        var container = ConfigureContainer();
 
        var client = container.Resolve<IMyService>("operations");
        for(int i=0;i<10;i++)
        {
            Console.WriteLine("Asking: " + i);
            client.BeginWcfCall(
                s => s.MyOperation("Operation for " + i.ToString()),
                asyncCall => Console.WriteLine(asyncCall.End()),
                null);
        }
        Console.ReadKey(true);
    }
 
    private static WindsorContainer ConfigureContainer()
    {
        var container = new WindsorContainer();
        container.AddFacility<WcfFacility>().Register(
            Component.For<IMyService>()
                .Named("operations")
                .ActAs(new DefaultClientModel
                           {
                               Endpoint = WcfEndpoint.
                                   BoundTo(new NetTcpBinding()).
                                   At("net.tcp://localhost/Service")
                           }));
        return container;
    }
}

It configures Windsor container and WCF Facility, then it obtains client proxy for the service (again, synchronous contract) and uses some WCF Facility magic to perform calls asynchronously. Let’s run it and see:

asyncwcf_program

As you can see, on the client we first issued all 10 requests, then we gradually received responses from the server.

So how do I use it?

Discussion

As you can see in the code above, instead of calling MyOperation directly on proxy I used BeginWcfCall extension method from WCF Facility and passed there a delegate with invocation of the method, plus two more arguments. The second and third arguments can be either AsyncCallback, and object, like in standard .NET async pattern, or (as in my example) IWcfAsyncCall<T> (or it’s non-generic version for methods that don’t return any value).

asyncwcf_IWcfAsyncCall

IWcfAsyncCall itself inherits from IAsyncResult and adds convenience methods to end async invocation. The overload with out arguments are there to handle methods that have ref or out arguments. Yes – this means you can use that also for methods with out and ref arguments. One more noteworthy feature is usage of SynchronisationContext, which means it’s safe to update WinForms/WPF GUI from the end operation thread.

The code is available now, take it, use it, tell us what you think.

Technorati Tags: ,

7 Thoughts.

  1. Hi Krzysztof, I didn’t get my hand dirty with that right now but the overall solution looks really clever!

    Kudos to Craig and you for such an overhaul!

  2. Hi,

    Thanks for that great work, it looks pretty and match perfectly a part of my current concerns.

    I am doing dynamic asynchronous WCF calls. Thanks to you, this is now easy… From classic .Net.

    Now, I consider porting your DPWCF.Proxy to Silverlight.
    What you think? Is that possible? What could be the major issues on the path?

  3. @Fred Legrain
    We looked at moving this to Silverlight, and decided it would be quite hard actually. First of all, Silverlight WCF works quite differently as compared to .NET version. This means the approach I used in my spike (DPWCF) won’t work at all – Silverlight just has no API it uses.

    You might try to build similar thing on top of Dynamic Proxy 2.2, but that would basically mean starting from scratch and since Silverlight 3.0 does not allow binaries sharing between SL and .NET you would have to share your contract at a code level, and compile them twice, which is not a big issue most of the time – rather inconvenience.

    If you’d like to help with this effort, go to castle users group and start a thread about it.

  4. That’s what I figured out quickly after putting your code in a Silverlight project.
    I plan going explore some other path that I have in mind.

    Thank you,
    Frederic

  5. The link to your project appears to be broken. Can you supply a valid path to the project? Thanks!

Comments are closed.