If you have created any silverlight applications which need to communicate to a WCF Endpoint you know that you must communicate with WCF via Async actions. As you may know there are multiple ways to get your Silverlight application to communicate with a WCF service. One way is to simply create a Service Reference, aka proxy. The other is to use the ChannelFactory and a shared interface. There are many good references on the net on why one is better than the other but personally I prefer to use the ChannelFactory approach as it allows for cleaner separation of concerns and it allows me to share the same interfaces/models on both side of the wire. Of course this really only works in a closed development scenario, one where you control both sides of the wire.
The issue with using the Channelfactory pattern is that by default it will create synchronous endpoints for use and this is not allowed in Silverlight. However this problem is easily solved for you and we will take a look at how to solve this.
When you typically setup a WCF OperationContract you typically do something like the following:
[OperationContract]
List<Make> GetMakes();
However as you may have guessed this will create a synchronous method/endpoint when you use the ChanelFactory. In order to use this in WCF you need to make a slight change to your setup. You will need to instruct the Contract that you are following the AsyncPattern . The code below will show you how using the AsyncPattern changes your setup.
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginGetMakes(AsyncCallback callback, object state);
IList<Make> EndGetMakes(IAsyncResult result);
As you can see from above, using the AsyncPattern = true pattern does mean we need to create 2 methods not just 1. We should take a closer look at these to better understand what is going one.
In the first method we are creating the ‘begin’ call which will return us back a IAsyncResult which will later be provided to the End method.
In the second method we are making a call and passing in the IAsyncResult instance provided to us via the begin call. It is this second method which will return us back our actual data.
Now that we know how to create our OperationContract we should take a look at how to create our implementation of these contracts. The code below will illustrate how to do this
public IAsyncResult BeginGetMakes( AsyncCallback callback, object state )
{
var db = new AutomobilesEntities();
var makes = db.Makes.Select( m => new Make {Name = m.Name} ).ToList();
return new CompletedAsyncResult>(makes);
}
public IList EndGetMakes( IAsyncResult result )
{
var completedAsyncResult = result as CompletedAsyncResult>;
return completedAsyncResult != null ? completedAsyncResult.Data : new List();
}
Taking a closer look above you should notice that the Begin method will actually do the ‘heavy’ lifting in terms of performing any real logic. This method also returns back a AsyncResult class (see below for that class) which will be passed into the ending call The End call. The End call will accept the provided AsyncResult instance and pull out the data payload inside of it. It will then pass this payload back as the actual results you were expecting.
Now that we know how to create an Async endpoint how to we spin up an endpoint via the ChannelFactory and make our WCF call work. Take a look at the code below for that example.
var binding = new BasicHttpBinding(BasicHttpSecurityMode.None)
{
MaxBufferSize = 2147483647,
MaxReceivedMessageSize = 2147483647
};
var endpointAddress = new EndpointAddress(fullEndpointAddress);
var channelFactory = new ChannelFactory(binding, endpointAddress);
var channel = channelFactory.CreateChannel();
AsyncCallback callback = result =>
{
var endGetMakes = channel.EndGetMakes(result);
Debug.WriteLine( "I Have Makes" );
};
channel.BeginGetMakes(callback, channel);
The magic in the code above which makes this all work is to create the pointer to the AsyncCallback instance and to provide this in your Begin call.
This is the class I used for encapsulating our IAsyncResult
public class CompletedAsyncResult : IAsyncResult
{
T data;
public CompletedAsyncResult(T data)
{ this.data = data; }
public T Data
{ get { return data; } }
#region IAsyncResult Members
public object AsyncState
{ get { return (object)data; } }
public WaitHandle AsyncWaitHandle
{ get { throw new Exception("The method or operation is not implemented."); } }
public bool CompletedSynchronously
{ get { return true; } }
public bool IsCompleted
{ get { return true; } }
#endregion
}
Using the Async Pattern in WCF is a bit more work but it will allow you to do 2 things. 1) Use the Channelfactory in WCF and 2) Create async service calls to allow your application to continue working while you wait for your call to come back.
Till next time,
Posted
12-17-2011 12:23 PM
by
Derik Whittaker