Update: this blog has been moved to http://brandonzeider.me/2010/microsoft-net/wcf-service-call-encapsulation-and-abstraction/
If you've worked with Windows Communication Foundation (WCF) very long, you know that it's an extremely powerful and flexible platform. Having the ability to consume WCF services in Silverlight makes using Silverlight for your business application much easier. However this does come with a few challenges.
WCF Service Calls Are Asynchronous
All calls to WCF services from Silverlight are asynchronous. This means that to call a service, you must instantiate the generated service client, handle the completed event for the service method you wish to call, call the service method using the generated asynchrouns method, then close the connection using the generated CloseAsync method (which is calling the OnBeginClose method of the ClientBase class).
WCF Service Clients Must Be Explicitly Closed
As mentioned above, after calling a WCF service, you must remember to call the CloseAsync method, otherwise the connection remains open. Even though the generated service client derives from ClientBase, which implements IDisposable, it is not safe to wrap the service client in a using statement and have the IDisposable.Dispose method kill the connection, you must call it explicitly.
Solution
Because of these challenges, and especially if the service is called in more than one class within your Silverlight application (for example more than one viewmodel), you've probably looked for or created a way to encapsulate these service calls and abstract the details of how the service is consumed from the rest of the application. With the aid of generics, we can create a base class that encapsulates the creation of a WCF service client, handling the result of the WCF service call, and closing the connection with IDisposable.
For this example we'll create a simple MathService that allows you to call Add, Subtract, Multiply and Divide. Here's the interface:
using System.ServiceModel;
namespace WcfServiceExample.Web.WCF
{
[ServiceContract]
public interface IMathService
{
[OperationContract]
decimal Add(params decimal[] args);
[OperationContract]
decimal Multiply(params decimal[] args);
[OperationContract]
decimal Subtract(decimal minuend, decimal subtrahend);
[OperationContract]
decimal Divide(decimal numerator, decimal denominator);
}
}
Once our WCF service is working, we can consume this service within our Silverlight application. Within our Silverlight development framework (or wherever you choose), create a base class for all service clients:
using System;
using System.ServiceModel;
namespace Framework.WCF
{
public abstract class ServiceBase<TClient, TChannel> : ClientBase<TChannel>, IDisposable
where TClient : ClientBase<TChannel>, new()
where TChannel : class
{
#region Fields
private readonly TClient _wcfServiceClient = null;
#endregion Fields
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ServiceBase<TClient, TChannel>"/> class.
/// </summary>
protected ServiceBase()
{
_wcfServiceClient = Activator.CreateInstance<TClient>();
}
#endregion Constructors
#region Properties
/// <summary>
/// Gets the WCF service client.
/// </summary>
/// <value>The WCF service client.</value>
protected TClient WcfServiceClient { get { return _wcfServiceClient; } }
#endregion Properties
#region Methods
/// <summary>
/// Handles the result of the WCF service call.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="action">The action.</param>
/// <param name="result">The result.</param>
public void HandleResult<T>(Action<T> action, T result)
{
if (action != null)
{
action(result);
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (_wcfServiceClient != null && _wcfServiceClient.State == CommunicationState.Opened)
{
try { _wcfServiceClient.InnerChannel.BeginClose(null, null); }
catch
{
_wcfServiceClient.Abort();
throw;
}
}
}
#endregion Methods
}
}
Notice that we are defining the channel and interface with the aid of generics, and closing our connection through the use of IDisposable. Next we can create a concrete class deriving from this base class, passing in the generated service client and interface that we want to abstract.
using System;
using System.Collections.ObjectModel;
using Framework.WCF;
namespace WcfServiceExample.Services
{
public sealed class MathService : ServiceBase<WebServices.MathService.MathServiceClient, WebServices.MathService.IMathService>
{
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="MathService"/> class.
/// </summary>
public MathService()
{
base.WcfServiceClient.SubtractCompleted += new EventHandler<WebServices.MathService.SubtractCompletedEventArgs>(WcfServiceClient_SubtractCompleted);
}
#endregion Constructors
#region Methods
/// <summary>
/// Encapsulates the call to MathServiceClient.Subtract
/// </summary>
/// <param name="callback">The callback.</param>
/// <param name="minuend">The minuend.</param>
/// <param name="subtrahend">The subtrahend.</param>
public void Subtract(Action<decimal> callback, decimal minuend, decimal subtrahend)
{
WcfServiceClient.SubtractAsync(minuend, subtrahend, callback);
}
/// <summary>
/// Handles the SubtractCompleted event of the WcfServiceClient control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="WebServices.MathService.SubtractCompletedEventArgs"/> instance containing the event data.</param>
private void WcfServiceClient_SubtractCompleted(object sender, WebServices.MathService.SubtractCompletedEventArgs e)
{
base.HandleResult<decimal>(e.UserState as Action<decimal>, e.Result);
}
#endregion Methods
}
}
Now that we have abstracted MathService, it's ready to be used. Here's where our "hard work" pays off. Instead of calling our WCF service the traditional way:
MathServiceClient mathServiceClient = new MathServiceClient();
mathServiceClient.SubtractCompleted += new System.EventHandler<SubtractCompletedEventArgs>(mathServiceClient_SubtractCompleted);
mathServiceClient.SubtractAsync(SubtractXValue, SubtractYValue);
mathServiceClient.CloseAsync();
We can call it this way:
using (MathService service = new MathService())
{
service.Subtract(HandleSubtractResult, SubtractXValue, SubtractYValue);
}
This is much cleaner, and our viewmodels do not need to know how the service is created, called, closed, etc. The big win in my book, however, is the ability to use IDisposable to close the service channel connection.
Download
WcfServiceExample.zip (534.83 kb)

This work is licensed under a Creative Commons Attribution 3.0 Unported License.
Tags: .NET,
MVVM,
Silverlight,
Design Patterns,
Windows Communication Foundation,
WCF
Categories: Design Patterns |
Microsoft .NET |
Silverlight |
Windows Communication Foundation