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:
 

16 Responses to Composing WCF services with MEF

  1. Ivan says:

    Hi Tim!

    I checked your solution against the one I created and noticed one difference. I have multi-tenant application and my server runs same service on different URL’s like so:

    foreach (var account in cmc.Accounts.Where(aa => aa.IsActive).ToList())
    {
    RouteTable.Routes.Add(
    new ServiceRoute(
    account.AccountId + “/mobile”, new MyServiceHostFactory(), typeof(MobileService)));
    }

    Where MyServiceHostFactory looks like this:

    public class MyServiceHostFactory : WebServiceHostFactory
    {

    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
    var host = new MyServiceHost(serviceType, baseAddresses);

    var catalog = new WebScopedCatalog(new DirectoryCatalog(@”.\bin”));
    host.Container = new CompositionContainer(catalog);

    return host;
    }
    }

    As you see – I was creating separate catalog for each container. This is what I thought I need for my purpose but now I realize that if I have ContextManager that aware of current request – I can safely share same service instance between routes. What do you think?

    Also, do you think your WebScopedCatalog will be of any help in WCF? The way I see it – I needed only one object that Request-aware and it can be solved with MEF attribute.

    Thanks for your input!

  2. Ivan says:

    Also, do you know if WCF creates separate instance of service object for each route? Would I get in trouble with threads if I start sharing same service instance between routes?

    • Tim Roberts says:

      If you’ve used PartCreationPolicy.Shared on your exported implementation, then that does imply that your code needs to be thread safe if it’s being shared between requests.

  3. John says:

    I’ve seen several examples of GetInstance implementation, all of them a similar, but one. Here it is:

    public object GetInstance(System.ServiceModel.InstanceContext instanceContext, System.ServiceModel.Channels.Message message)
    {
    var serviceType = instanceContext.Host.Description.ServiceType;
    var instance = Activator.CreateInstance(serviceType);

    ComposeInstance(instance);
    return instance;
    }

    private void ComposeInstance(object instance)
    {
    lock (_synclock)
    {
    CompositionBatch batch = new CompositionBatch();
    batch.AddPart(instance);
    try
    {
    _container.Compose(batch);
    }
    catch (CompositionException compositionException)
    {
    Trace.WriteLine(compositionException.ToString());
    }
    catch (Exception err)
    {
    Trace.WriteLine(err.ToString());
    }
    }
    }

    Seems interesting to me, as it uses synchronization. Unfortunately, I have little knowledge about mef, so don’t realy know what implementation is right.

    • Tim Roberts says:

      The synchronization is being used here because the instance provider implementation is modifying the contents of the CompositionContainer object which will be shared. I don’t like this particular approach because it’s not the instance provider’s responsibility to update the CompositionContainer.

  4. programmer1 says:

    Hi,

    I’m trying to implement your code as above example but I’m quite new to all this.

    I’ve created all the classes as per your example, and I have a WCF service in place within my project.
    Where I am stuck is how do I gain access to the methods/types declared on my WCF service by using your class methods?

    I’ve put the code into my global.asax as suggested as my service is .svc

    I thought I had to use “host.SingletonInstance” to gain access to the service, where “host” is a ComposedServiceHost object

    Any help would be very much appreciated

    • Tim Roberts says:

      I’m not sure what you’re trying to do when you say that you want to access the methods/types declared on your WCF service. Could you provide a little more detail?

      The code in this post shows you how you can compose your WCF service implementation with MEF. For example, you could have MEF inject an ILogger implementation into your WCF service (like you can do with other Inversion of Control containers).

      If you want to access the “type” of the WCF service being hosted, then this is passed into the ComposedServiceHostFactory class, and you can using reflection on that as normal.

    • Karcy says:

      TYVM you’ve sovled all my problems

  5. I enjoy, cause I found exactly what I was having a look for. You have ended my 4 day lengthy hunt! God Bless you man. Have a nice day. Bye

  6. Magnificent goods from you, man. Composing WCF services with MEF I have understand your stuff previous to and you’re just too great. I really like what you have acquired here, really like what you are saying and the way in which you say it. You make it enjoyable and you still take care of to keep it wise. I can not wait to read far more from you. This is really a tremendous Composing WCF services with MEF informations.

  7. Phil says:

    Hi Tim, having followed your example and ensuring there is a valid Export on my service, looking at the following line:

    Export export = GetServiceExport();

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

    export here is always returning null even though the part exists in the catalog. Any ideas?

    • Phil says:

      Upon further investigation, in the code below, AttributedModelServices.GetContractName(_serviceType) is returning the service name not the interface contract, so GetExports returns null. If I hard code that parameter to the fully qualified interface, then it works as expected.

      var importDefinition = new ContractBasedImportDefinition(
      AttributedModelServices.GetContractName(_serviceType),
      AttributedModelServices.GetContractName(_serviceType),
      null,
      ImportCardinality.ZeroOrMore,
      false,
      false,
      CreationPolicy.Any);

  8. Joe Shooter says:

    Hey Tim,

    great stuff this MEF with WCF. Do you have any more articles on this and it is useable in a production system?

    Reason I’m asking is we have an architect using your MEF factory and service host (custom), etc.. However, it would be nice to know a good source of information regarding MEF and WCF.

    thanks! joe

Leave a Reply

Your email address will not be published. Required fields are marked *