Asynchronous WCF Calls with .NET Framework 4.0

When we want the possibility to make asynchronous calls to web services with WCF, we can check “Generate asynchronous operations” when adding a service reference. But I usually hand-code my proxies. Here is a recipe for adding async to hand-coded proxies.

Say I have an interface like this:

[ServiceContract]
public interface IMyInterface
{
	[OperationContract(Name="Foo", Action="urn:MyAction", ReplyAction="")]
	Response Foo(Request request);
}

Create two more methods, BeginFoo and EndFoo. BeginFoo must have the same operation contract name, action and reply action as Foo:

        [OperationContract(Name = "Foo", Action = "MyAction", ReplyAction = "", AsyncPattern = true)]
        System.IAsyncResult BeginFoo(Request request, System.AsyncCallback callback, object asyncState);

        Response EndFoo(System.IAsyncResult result);

Then, create the proxy like this:

    public class MyProxy : ClientBase<IMyInterface>, IMyInterface
    {
        private BeginOperationDelegate onBeginFooDelegate;

        private EndOperationDelegate onEndFooDelegate;

        private System.Threading.SendOrPostCallback onFooCompletedDelegate;

        public event System.EventHandler<FooCompletedEventArgs> FooCompleted;

        public Response Foo(Request request)
        {
            return Channel.Foo(request);
        }

        public IAsyncResult BeginFoo(Request request, AsyncCallback callback, object asyncState)
        {
            return Channel.BeginFoo(request, callback, asyncState);
        }

        public Response EndFoo(IAsyncResult result)
        {
            return Channel.EndFoo(result);
        }

        private System.IAsyncResult OnBeginFoo(object[] inValues, System.AsyncCallback callback, object asyncState)
        {
            var request = (Request)inValues[0];
            return this.BeginFoo(request, callback, asyncState);
        }

        private object[] OnEndFoo(System.IAsyncResult result)
        {
            var retVal = this.EndFoo(result);
            return new object[] {retVal};
        }

        private void OnFooCompleted(object state)
        {
            if ((this.FooCompleted != null))
            {
                InvokeAsyncCompletedEventArgs e = ((InvokeAsyncCompletedEventArgs)(state));
                this.FooCompleted(this, new FooCompletedEventArgs(e.Results, e.Error, e.Cancelled, e.UserState));
            }
        }

        public void FooAsync(Request request)
        {
            this.FooAsync(request, null);
        }

        public void FooAsync(Request request, object userState)
        {
            if ((this.onBeginFooDelegate == null))
            {
                this.onBeginFooDelegate = new BeginOperationDelegate(this.OnBeginFoo);
            }
            if ((this.onEndFooDelegate == null))
            {
                this.onEndFooDelegate = new EndOperationDelegate(this.OnEndFoo);
            }
            if ((this.onFooCompletedDelegate == null))
            {
                this.onFooCompletedDelegate = new System.Threading.SendOrPostCallback(this.OnFooCompleted);
            }
            base.InvokeAsync(this.onBeginFooDelegate, new object[] {
                    request}, this.onEndFooDelegate, this.onFooCompletedDelegate, userState);
        }
    }

    public class FooCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
    {
        private object[] results;

        public FooCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) :
            base(exception, cancelled, userState)
        {
            this.results = results;
        }

        public Response Result
        {
            get
            {
                base.RaiseExceptionIfNecessary();
                return ((Response)(this.results[0]));
            }
        }
    }

Here is how to use this proxy:

private void CallMyProxy()
{
    ...
    var proxy = new MyProxy();
    proxy.FooCompleted += new EventHandler<FooCompletedEventArgs>(FooCallback);
    proxy.FooAsync(request);
}

private void FooCallback(object sender, FooCompletedEventArgs e)
{
    try {
        var response = e.Result;
        ...
    }
    finally
    {
        var proxy = (MyProxy)sender;
        if (proxy.State == CommunicationState.Opened)
            proxy.Close();
    }
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s