In an earlier post I showed how it was possible to extend MEF with a custom part creation policy that tied the lifetime of an exported part to the lifetime of a HTTP request or session. Another common requirement I’ve encountered many times is how to make WCF services compose-able, and in this post, I’ll show you how I achieve this by writing a custom WCF instance provider.

An instance provider is what WCF uses to create a service object, so it makes sense to start there.  The IInstanceProvider interface defines two methods: GetInstance() (+1 overload) and ReleaseInstance(). We need to implement these so that our instance provider is MEF-aware and delegates the creation of the service object to MEF allowing it to satisfy all of the object’s import requirements through composition. Let’s begin then, by creating a new ComposedInstanceProvider class:


public class ComposedInstanceProvider : IInstanceProvider
{
private readonly Type _serviceType;
private readonly CompositionContainer _container;

public ComposedInstanceProvider(Type serviceType, CompositionContainer container)
{
_serviceType = serviceType;
_container = container;
}

public object GetInstance(InstanceContext context)
{
Export export = GetServiceExport();

if (export == null)
throw new InvalidOperationException();

return export.Value;
}

public object GetInstance(InstanceContext context, Message message)
{
return GetInstance(context);
}

public void ReleaseInstance(InstanceContext context, object instance)
{
var disposable = instance as IDisposable;

if (disposable != null)
disposeable.Dispose();
}

private Export GetServiceExport()
{
var importDefinition
= new ContractBasedImportDefinition(AttributedModelServices.GetContractName(_serviceType),
AttributedModelServices.GetTypeIdentity(_serviceType),
null,
ImportCardinality.ZeroOrMore,
true,
true,
CreationPolicy.Any);

return _container.GetExports(importDefinition).FirstOrDefault();
}
}

This is essentially the core to making WCF service compose-able. The GetInstance() implementations that are invoked by WCF simply use a supplied CompositionContainer object to get an implementation of the service type. So far so good. However, we need to plug our custom instance provider into WCF, and that means implementing a few other WCF pieces. First we need a custom service behaviour (implementing IServiceBehavior), that allows us to extend the service with our compose-able behaviour. It does this by replacing the default instance provider with a ComposedInstanceProvider. I tend to think of service behaviours as aspects or cross-cutting features that are applied across the entire service, and compose-ability fits in nicely with this metaphor. We’ll also need a custom service host (a class derived from ServiceHost), that will apply the service behaviour to the service that it is hosting.

If we want to use .svc files to host our services in IIS, then we will also need to implement a custom service host factory (a class derived from ServiceHostFactory) which is responsible for creating instances of our derived ServiceHost class. Since we started in the middle of the stack with our custom instance provider, let’s move up a level and implement the custom service behaviour:


public class ComposedServiceBehavior : IServiceBehavior
{
private readonly IInstanceProvider _instanceProvider;

public ComposedServiceBehavior(Type serviceType, CompositionContainer container)
{
_instanceProvider = new ComposedInstanceProvider(serviceType, container);
}

public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{ }

public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{ }

public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcherBase dispatcher in serviceHostBase.ChannelDispatchers)
{
var channelDispatcher = dispatcher as ChannelDispatcher;

if (channelDispatcher == null)
continue;

foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
{
endpointDispatcher.DispatchRuntime.InstanceProvider = _instanceProvider;
}
}
}
}

The behaviour implementation is again very simple. It creates a new ComposedInstanceProvider object using the supplied service type and CompositionContainer objects, and then replaces it with the default one in the endpoint dispatcher. Whenever WCF requires a service object to be created, it will now be using our own MEF-aware instance provider.

Next, we need a custom service host that will apply our behaviour. Again, this class is very simple:


public class ComposedServiceHost : ServiceHost
{
private readonly Type _serviceType;
private readonly CompositionContainer _container;

public ComposedServiceHost(Type serviceType, CompositionContainer container, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
_serviceType = serviceType;
_container = container;
}

protected override void OnOpening()
{
if (Description.Behaviors.Find<ComposedServiceBehavior>() == null)
{
Description.Behaviors.Add(new ComposedServiceBehavior(_serviceType, _container));
}

base.OnOpening();
}
}

Our custom service host simply adds our ComposedServiceBehavior implementation to the service description if it has not already been done so. If you’re self-hosting your services in a Windows Service for example, then that’s all you need to do. Instead of creating a ServiceHost object when your application starts, you simply create a new ComposedServiceHost and pass in the CompositionContainer that it should use to satisfy the imports.

If you use .svc files to host your WCF services in IIS, then you’ll need to use a custom service host factory that knows how to create ComposedServiceHost objects. Once you’ve implemented the custom service host factory as shown below:


public class ComposedServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
CompositionContainer container = ComposedServiceHosts.CompositionContainer;

if (container == null)
throw new InvalidOperationException();

return CreateComposedServiceHost(serviceType, baseAddresses, container);
}

protected virtual ComposedServiceHost CreateComposedServiceHost(Type serviceType, Uri[] baseAddresses, CompositionContainer container)
{
return new ComposedServiceHost(serviceType, container, baseAddresses);
}
}

you can then modify the .svc file so that it uses it in the Factory attribute:


<% @ServiceHost Service="FooService" Factory="ComposedServiceHostFactory" %>

The factory is created by the WCF hosting runtime, which prevents us from passing in the CompositionContainer we need to compose our WCF services. To get around this, we’ll need a utility that provides this for us when the WCF runtime invokes the factory. You can see being used in the overridden CreateServiceHost() method above where it obtains the container from a ComposedServiceHosts utility class.


public static class ComposedServiceHosts
{
private static CompositionContainer _container = null;

internal static CompositionContainer CompositionContainer
{
get { return _container; }
}

public static void SetCompositionContainer(CompositionContainer container)
{
_container = container;
}
}

We can then create our CompositionContainer in the Global.asax file (as we would normally do for any web-based project), and set-up the ComposedServiceHosts utility class so that it is used for all our composed service operations:


protected void Application_Start(object sender, EventArgs e)
{
var catalogs
= new AggregateCatalog(new[]
{
new AssemblyCatalog(typeof(FooService).Assembly),
new AssemblyCatalog(GetType().Assembly)
});

ComposedServiceHosts.SetCompositionContainer(new CompositionContainer(catalogs));
}

Tagged with: