Introduction
This article looks into a possible solution for handling the Faulted state and Closure of a WCF service when being used in a stateful application.
The solution uses a Dynamic Proxy approach to handle the issue without breaking the original service contract. This enables a ViewModel to be dependent on
the service interface without knowing it is actually using WCF.
Background
Consider a ViewModel which has the WCF service injected into it. There are a number of
gotcha’s:
- How do we deal with the service when it becomes faulted, and the user is still using the window?
- How do we ensure the service is cleaned up correctly?
- How do we do the above
two, whist working with the service interfaces? (Not knowing it’s actually a WCF service.)
To expand on Point 3, it wants to make use of the interface which the service exposes (either a generated file or from a shared DLL) and it also wants to provide ease of unit testing (some mocking frameworks will not work with concrete classes).
This post explores a solution using Dynamic Proxies. (Please provide any feedback if you
know of another way.)
Side note:
Some IoC containers such as Castle Windsor, has a facility for all of this. Check out what your container does first. If it does not support handling of the WCF client channel, then this solution should work for you.
Side Side note:
Silverlight forces the user to implement the interface with async methods (there are some
work a rounds, this solution can be used with them as well). This article will concentrate on WPF, with a Silverlight version is contained in example solution.
How to Use
If you want to jump into using this here is what you will need to do:
- Add a reference to Castle.Core
- Download the WcfChannelProxy.zip project, and copy both of the files into your project
- In the application when you create your view, create the required service dependency (either directly via the WcfClientFactory or using an IoC Container) and inject the instance into your ViewModel.
_service1 = new WcfProxy.WcfClientFactory().CreateClient<IService1>();
MainViewModel vm = new MainViewModel(_service1);
MainWindow window = new MainWindow(vm);
To dispose of the service, that is simple too:
((IDisposable) _service1).Dispose();
The rest of this article discusses how the solution works and supplies a little more context.
VS Sample Sln Overview
A sample VS Sln has been provided to demonstrate the use of the solution and also highlight where constraints are applied. The solution is made of
five projects, as follows:
- WCF host – web application, which hosts the WCF services (this is a file new WCF service project, with the addition of a client policy to allow the Silverlight client access)
- WPF client – a simple WPF client
- Sl client – simple Silverlight client, using version 5
- Sl host – web application which hosts the Silverlight XAP proj
- Test – tests the interceptor of the dynamic project.
Both client solutions make use of the Dynamic Proxy (Wcf Proxy), to handle calling the web services. They both use the generated service reference, however the solution will also work with a shared DLL approach (where a seperate DLL contains the service interfaces and DTO's).
Overview of the Solution
The solution uses Castle’s Dynamic Proxy (WcfProxy) to provide the implementation of the service interface and shield the ViewModel from the channel maintenance.
The Service
The aim is for the client to inject the interface of the services so the ViewModel will only to know of their operations. Here is the sample Service:
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
}
There is no Close
, Abort
, or Dispose
methods. Close and Abort can be made available to the client via generating a
ServiceClient
class (which is not an interface, harder to mock and test with) or by using the Channel directly (however, the consuming class would know of WCF directly).
The ViewModel
What the ViewModel depends on is key; the dependency interfaces will define the operations available to a ViewModel. Given we have Service1
(defined above) being injected to the ViewModel, this will mean the ViewModel cannot regulate the channel.
Below is the ViewModel, which is dependent on Service1
, via constructor injection.
public class MainViewModel : INotifyPropertyChanged
{
private readonly IService1 _service1;
private string _result;
public MainViewModel(IService1 service1)
{
_service1 = service1;
GetDataCmd = new DelegateCommand<object>(GetData);
PropertyChanged += delegate { };
}
public string Result
{
get { return _result; }
set
{
_result = value;
PropertyChanged(this, new PropertyChangedEventArgs("Result"));
}
}
public ICommand GetDataCmd { get; private set; }
private void GetData(object o)
{
Result = _service1.GetData(123);
}
public event PropertyChangedEventHandler PropertyChanged;
}
The Proxy and Channel Factory Manager
The use of a, slightly modified,
ChannelFactoryManager
class.
This is used to create and maintain Channel Factories and create new instances of Channels.
The solution constructs a Dynamic Proxy which implements the service interface, in this case
IService1
. The proxy instance has a WCF channel for the Service, which it maintains. When the channel becomes faulted, the proxy will clean up the faulted channel and request a new channel from the ChannelFactoryManager
.
public class WcfClientFactory
{
private static readonly ProxyGenerator Generator = new ProxyGenerator();
private readonly IChannelFactoryManager _manager = new ChannelFactoryManager();
public T CreateClient<T>() where T : class
{
var service = _manager.CreateChannel<T>();
var proxy = Generator.CreateInterfaceProxyWithTargetInterface(typeof(T),
new[] { typeof(IDisposable) }, service, new WcfClientInterceptor<T>(service));
return (T)proxy;
}
}
public class WcfClientInterceptor<TService> : IInterceptor where TService : class
{
private readonly IChannelFactoryManager _manager;
private TService _instance;
private bool _instanceChanged;
public WcfClientInterceptor(TService service)
: this(service, new ChannelFactoryManager())
{
}
public WcfClientInterceptor(TService service, IChannelFactoryManager manager)
{
_manager = manager;
SetupInsance(service);
_instanceChanged = false;
}
public void Intercept(IInvocation invocation)
{
if (_instanceChanged)
{
var cpt = (IChangeProxyTarget)invocation;
cpt.ChangeProxyTarget(_instance);
cpt.ChangeInvocationTarget(_instance);
_instanceChanged = false;
}
if (invocation.Method.Name == "Dispose")
{
Dispose();
return;
}
invocation.Proceed();
}
private void Dispose()
{
ICommunicationObject commObj = (ICommunicationObject)_instance;
commObj.Faulted -= OnFaulted;
try
{
if (commObj.State == CommunicationState.Faulted)
{
commObj.Abort();
}
else
{
commObj.Close();
}
}
catch
{
commObj.Abort();
}
_instance = null;
}
private void SetupInsance(TService service)
{
_instance = service;
((ICommunicationObject)_instance).Faulted += OnFaulted;
_instanceChanged = true;
}
private void CreateNewChannel()
{
var newInstance = _manager.CreateChannel<TService>();
SetupInsance(newInstance);
}
private void OnFaulted(object sender, EventArgs eventArgs)
{
Dispose();
CreateNewChannel();
}
}
The proxy ensures there will always be a valid channel for the ViewModel to use.
The proxy has also been extended to support IDisposable
, allowing for an IoC container to clean up the proxy when it is released.
Some Tests
The following screenshot are the tests included in the sample project. They focus on the Proxy to try and ensure the interceptor works correctly.
Points of Interest
- We can now inject our services using their interfaces
- This makes Mocking easier
- The ViewModel does not know of any Channel maintenance
- We have an
IDisposable
interface implementation
- This makes cleaning up the resources slightly easier; also if you are using an IoC container, they can make use of this for you.
The solution is only intended to be used in a stateful WPF/Silverlight application.
History