This article is compatible with the latest version of Silverlight.
This article is Part 1 of the series Deep Dive Into WCF.
1. Introduction
As many other Microsoft libraries and frameworks Windows Communication Foundation requires some workarounds to be used in the project, which is designed in test driven development style. The main problem is inability to use Dependency Injection pattern, which is the heart of test driven development. In this article I will show you a set of tricks for both Silverlight and .NET runtimes, which will allow you to use WCF inside TDD project.
Download source code
2. Content
2.1. Solution overview
To achieve this we need to solve the following problems:
1. WCF does not support dependency injection by default. I will solve this problem by creating custom WCF service factory, which will use Microsoft Practices & Patterns Common Service Locator to inject service dependencies.
2. Generated service proxy client class has no interface therefore it cannot be mocked. I will completely give up using generated service proxy and will use channel factory instead.
2.2. Dependency Injection WCF service factory
To illustrate the typical case of using dependency injection in WCF, lets assume that we have IProductService service contract, which implementation depends on ProductRepository. You should think about ProductRepository as a class that uses database or ORM to retrieve objects. To prevent this unwanted dependency I will extract IProductRepository to mock it during ProductService tests. (In my example product repository and product service interfaces are absolutely the same, it was made due to simplicity reasons only).
IProductService:
[ServiceContract]
public interface IProductService
{
[OperationContract]
Product GetProductByName(string productName);
}
ProductService:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ProductService : IProductService
{
private IProductRepository productRepository;
public ProductService(IProductRepository productRepository)
{
this.productRepository = productRepository;
}
#region IProductService Members
public Product GetProductByName(string productName)
{
return this.productRepository.GetProductByName(productName);
}
#endregion
}
By default instances of ProductService are created by the activator so we cannot inject implementation of ProductRepository there.
The extending of WCF is not straightforward task. To enable dependency injection we should create custom service factory, which will use common service locator instead of the activator to create service instances. To achieve this you should perform the following steps:
1. Create custom ServiceHostFactory, which will create DI-enabled service hosts.
/// <summary>
/// Produces <see cref="DependencyInjectionHost"/>s.
/// </summary>
public class DependencyInjectionHostFactory : ServiceHostFactory
{
/// <summary>
/// Creates a <see cref="DependencyInjectionHost"/> for a specified type of service with a specific base address.
/// </summary>
/// <returns>
/// A <see cref="DependencyInjectionHost"/> for the type of service specified with a specific base address.
/// </returns>
/// <param name="serviceType">
/// Specifies the type of service to host.
/// </param>
/// <param name="baseAddresses">
/// The <see cref="T:System.Array"/> of type <see cref="T:System.Uri"/> that contains the base addresses for the service hosted.
/// </param>
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new DependencyInjectionHost(serviceType, baseAddresses);
}
}
2. Create custom service host (DI-enabled service host), which will apply DI behavior by overriding OnOpening method. You could also implement classes to apply this behavior using custom attributes or via config.
/// <summary>
/// Dependency injection service host.
/// </summary>
public class DependencyInjectionHost : ServiceHost
{
/// <summary>
/// Initializes a new instance of the <see cref="DependencyInjectionHost"/> class.
/// </summary>
public DependencyInjectionHost()
: base()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DependencyInjectionHost"/> class.
/// dependency injection host.
/// </summary>
/// <param name="serviceType">
/// </param>
/// <param name="baseAddresses">
/// </param>
public DependencyInjectionHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
}
/// <summary>
/// Invoked during the transition of a communication object into the opening state.
/// </summary>
protected override void OnOpening()
{
this.Description.Behaviors.Add(new DependencyInjectionServiceBehavior());
base.OnOpening();
}
}
3. Create DI service behavior, which plugs in DI-enabled instance provider at the right place at the right time. (How Deep does The Rabbit Hole Go? =)) )
/// <summary>
/// Dependency injection service behavior.
/// </summary>
public class DependencyInjectionServiceBehavior : IServiceBehavior
{
/// <summary>
/// Provides the ability to pass custom data to binding elements to support the contract implementation.
/// </summary>
/// <param name="serviceDescription">
/// The service description of the service.
/// </param>
/// <param name="serviceHostBase">
/// The host of the service.
/// </param>
/// <param name="endpoints">
/// The service endpoints.
/// </param>
/// <param name="bindingParameters">
/// Custom objects to which binding elements have access.
/// </param>
public void AddBindingParameters(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{
}
/// <summary>
/// Provides the ability to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects.
/// </summary>
/// <param name="serviceDescription">
/// The service description.
/// </param>
/// <param name="serviceHostBase">
/// The host that is currently being built.
/// </param>
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
var cd = cdb as ChannelDispatcher;
if (cd != null)
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
ed.DispatchRuntime.InstanceProvider =
new DependencyInjectionInstanceProvider(serviceDescription.ServiceType);
}
}
}
}
/// <summary>
/// Provides the ability to inspect the service host and the service description to confirm that the service can run successfully.
/// </summary>
/// <param name="serviceDescription">
/// The service description.
/// </param>
/// <param name="serviceHostBase">
/// The service host that is currently being constructed.
/// </param>
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
4. And finally create DI instance provider by implementing IInstanceProvider interface, that allows us to serve up instances of our service every time when WCF needs a new instance. All instances of service will be resolved by common service locator, which is used as an abstraction over DI containers to allow you to use the container you like the most.
/// <summary>
/// Dependency injection instance provider.
/// </summary>
public class DependencyInjectionInstanceProvider : IInstanceProvider
{
/// <summary>
/// WCF service type.
/// </summary>
private readonly Type serviceType;
/// <summary>
/// Initializes a new instance of the <see cref="DependencyInjectionInstanceProvider"/> class.
/// </summary>
/// <param name="serviceType">
/// The service type.
/// </param>
public DependencyInjectionInstanceProvider(Type serviceType)
{
this.serviceType = serviceType;
}
/// <summary>
/// Returns a service object given the specified <see cref="T:System.ServiceModel.InstanceContext"/> object.
/// </summary>
/// <returns>
/// A user-defined service object.
/// </returns>
/// <param name="instanceContext">
/// The current <see cref="T:System.ServiceModel.InstanceContext"/> object.
/// </param>
public object GetInstance(InstanceContext instanceContext)
{
return this.GetInstance(instanceContext, null);
}
/// <summary>
/// Returns a service object given the specified <see cref="T:System.ServiceModel.InstanceContext"/> object.
/// </summary>
/// <returns>
/// The service object.
/// </returns>
/// <param name="instanceContext">
/// The current <see cref="T:System.ServiceModel.InstanceContext"/> object.
/// </param>
/// <param name="message">
/// The message that triggered the creation of a service object.
/// </param>
public object GetInstance(InstanceContext instanceContext, Message message)
{
IServiceLocator serviceLocator = ServiceLocator.Current;
return serviceLocator.GetInstance(this.serviceType);
}
/// <summary>
/// Called when an <see cref="T:System.ServiceModel.InstanceContext"/> object recycles a service object.
/// </summary>
/// <param name="instanceContext">
/// The service's instance context.
/// </param>
/// <param name="instance">
/// The service object to be recycled.
/// </param>
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
}
}
To use a created dependency injection service factory with a service implementation, we must add some additional metadata to the service’s .svc file.
Example: ProductService.svc:
<%@ ServiceHost Language="C#" Debug="false" Service="Services.ProductService" Factory="WCFFacility.DependencyInjectionHostFactory" %>
Here is the example of bootstrapper* class, which will register IProductRepository implementation that should be injected to our ProductService.
public class Bootstrapper
{
public void Run()
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IProductRepository, ProductRepository>();
container.RegisterType<IServiceLocator, UnityServiceLocatorAdapter>(new ContainerControlledLifetimeManager());
ServiceLocator.SetLocatorProvider(() => container.Resolve<IServiceLocator>());
}
}
* – Bootstrapper is the place where all dependencies are injected. For ASP.NET it could be run inside an application start up event in Global.asax:
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
var bootStrapper = new Bootstrapper();
bootStrapper.Run();
}
}
2.3. TDD friendly WCF proxies.
I have successfully created our product service, so now it is time to use it at our Silverlight application. I won’t use code generated proxy, because it has no interface and cannot be mocked. Instead of that I will share Product and IProductService service contract by creating linked Silverlight project using Microsoft Project Linker, which will synchronize all changes made to them (I have used the same technique in my previous article, you can read more about it there). And then I will generate dynamic WCF proxy using channel factory by supplying service contract type as generic parameter there:
var channelFactory = new ChannelFactory<IProductService>(
new CustomBinding(
new BinaryMessageEncodingBindingElement(),
new HttpTransportBindingElement
{
MaxBufferSize = 2147483647,
MaxReceivedMessageSize = 2147483647,
}),
new EndpointAddress(new Uri(Application.Current.Host.Source, @"../ProductService.svc/")));
IProductService productService = channelFactory.CreateChannel();
Nice! But it won’t work =) May be you have noticed that I have created instance of synchronous service contract that is not supported in Silverlight. So we need to create another asynchronous variant of our service contract:
[ServiceContract(Name = "IProductService")]
public interface IProductServiceAsync
{
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginGetProductByName(string productName, AsyncCallback callback, object userState);
Product EndGetProductByName(IAsyncResult asyncResult);
}
Your inner voice says: Oh my god! I should implement asynchronous operations! That is awful! =((
To tell you the truth you shouldn’t =) WCF will decide by itself how to match asynchronous contract with synchronous implementation (thanks to Oren Eini, who wrote about it). To connect asynchronous service contract with synchronous contract that will be used instead, you should add ServiceContractAttribute with Name of that contract.
So the correct version of using channel factory will be the following:
var channelFactory = new ChannelFactory<IProductServiceAsync>(
new CustomBinding(
new BinaryMessageEncodingBindingElement(),
new HttpTransportBindingElement
{
MaxBufferSize = 2147483647,
MaxReceivedMessageSize = 2147483647,
}),
new EndpointAddress(new Uri(Application.Current.Host.Source, @"../ProductService.svc/")));
IProductServiceAsync productService = channelFactory.CreateChannel();
Now you should register this implementation of IProductServiceAsync in dependency injection container (I will use Unity as DI container). It is not so straightforward, because it is generated by factory method.
The first idea that came to my mind was to use unity container RegisterInstance method, but it was a mistake, because in this case our service will be resolved as singleton.
The right way to do this is to set up container to call channel factory CreateChannel method every time we need to resolve IProductServiceAsync.
It can be done with Unity Static Factory, which allows to register factory delegate for specified interface.
var container = new UnityContainer();
container.AddNewExtension<StaticFactoryExtension>();
container
.Configure<IStaticFactoryConfiguration>()
.RegisterFactory<IProductServiceAsync>(
new FactoryDelegate(c => channelFactory.CreateChannel()));
var productService = container.Resolve<IProductServiceAsync>();
* – there is a strange bug on my machine: resharper marks Microsoft.Practices.Unity.StaticFactory class with red as if they don't exist, but the build is successful.
Also you should remember that proxy object created by the channel should be closed after usage:
productService.BeginGetProductByName("foo",
ar =>
{
var result = productService.EndGetProductByName(ar);
(productService as ICommunicationObject).Close();
},null);
3. Summary
I hope you have enjoyed the article. If you find this article interesting I strongly recommend you to also look also Castle Windsor WCF Integration Facility project. It uses the same principle and has some additional capabilities, but in spite of my solution it can be used with Castle Windsor Container only.
Soon I’m going to finish another article in which I will share my experience with WCF authorization and error handling. It would be the second part of this article series.
If you have any questions or you have found a bug, please write a comment or e-mail me through my blog contact form.
4. Links
Here are some useful links, which could be a good start to learn Unity, Common Service Locator, Microsoft Project Linker, TDD and Dependency Injection.
1. Last update of Unity to work with Silverlight 3
2. MSDN: Project Linker: Synchronization Tool
3. The Art of Unit Testing with Examples in .NET by Roy Osherove.
4. Dependency Injection by Dhanji R. Prasanna