Integrating StructureMap with WCF


When developing with an IoC container like StructureMap, eventually some place in your code you will need to call the registry to instantiate your classes with their dependencies.  With StructureMap, this means a call to ObjectFactory.GetInstance.  Ideally, we’d like to limit the number of places the registry is called, so that we don’t have a lot of StructureMap concerns sprinkled throughout our application.

Suppose we have the following WCF service:

public class CustomerSearchService : ICustomerSearchService
{
    private readonly ICustomerRepository _customerRepository;
    private readonly ICustomerSummaryMapper _customerSummaryMapper;

    public CustomerSearchService(ICustomerRepository customerRepository, ICustomerSummaryMapper customerSummaryMapper)
    {
        _customerRepository = customerRepository;
        _customerSummaryMapper = customerSummaryMapper;
    }

    public CustomerSearchResult FindCustomerByName(string fullName)
    {
        Customer customer = _customerRepository.FindCustomerByName(fullName);

        if (customer == null)
        {
            return new CustomerSearchResult
                       {
                           IsSuccessful = false,
                           FailureReasons = new[] {"Customer not found."}
                       };
        }

        CustomerSummary summary = _customerSummaryMapper.MapFrom(customer);

        return  new CustomerSearchResult {IsSuccessful = true, Result = summary};
    }
}

Nothing too exciting, just a search service that returns customer summary information from a name search.  Now, if we just try to use this service as is, we get a fun exception:

The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host.

That makes sense, as WCF is in charge of instantiating my service class (CustomerSearchService), but it only knows how to call a no-args constructor.  For a quick fix, we can add this constructor:

public CustomerSearchService()
    : this(ObjectFactory.GetInstance<ICustomerRepository>(), 
        ObjectFactory.GetInstance<ICustomerSummaryMapper>())
{
}

Each of our WCF services would need to the same thing, have a bunch of ObjectFactory calls to set up the dependencies appropriately.  But we can find a better way, and have all of our services wired up automatically, with only one call to StructureMap in the entire application.  To do this, we’ll need to plug in to a few WCF extension points to make it happen.

A custom instance provider

WCF provides an interface just for this purpose, IInstanceProvider, that allows us to create custom instantiation behavior.  This interface has basically two methods:

  • CreateInstance – needs to return the right service
  • ReleaseInstance – if we have some custom cleanup to do

Just creating the IInstanceProvider isn’t enough, we’ll have to tell WCF to use our instance provider, instead of its own default instance provider.  This requires a custom endpoint behavior.  We can’t configure the instance provider directly, through configuration or other means.  Instead, we’ll use a custom service behavior through the IServiceBehavior interface.  Here’s the implementation of our custom service behavior:

public class StructureMapServiceBehavior : IServiceBehavior
{
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
        {
            ChannelDispatcher cd = cdb as ChannelDispatcher;
            if (cd != null)
            {
                foreach (EndpointDispatcher ed in cd.Endpoints)
                {
                    ed.DispatchRuntime.InstanceProvider = 
                        new StructureMapInstanceProvider(serviceDescription.ServiceType);
                }
            }
        }
    }

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

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

For all of the endpoints in all of the channels, we need to give WCF our custom instance provider.  Also, since StructureMap needs to know what type to create, we have to pass that information along to our instance provider.  This comes from the ServiceDescription passed in above.

With our service behavior done, let’s create the actual instance provider, which will call StructureMap:

public class StructureMapInstanceProvider : IInstanceProvider
{
    private readonly Type _serviceType;

    public StructureMapInstanceProvider(Type serviceType)
    {
        _serviceType = serviceType;
    }

    public object GetInstance(InstanceContext instanceContext)
    {
        return GetInstance(instanceContext, null);
    }

    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        return ObjectFactory.GetInstance(_serviceType);
    }

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
    }
}

Pretty straightforward, we just capture the service type (passed in from our service behavior earlier), then use ObjectFactory to create an instance of that service when asked.  There’s no information on the InstanceContext about the service type, or we could have just used it instead.

Now that we have our IInstanceProvider and IServiceBehavior, it’s time to hook up this new service behavior to the rest of WCF.  We have a few choices:

  • Attributes
  • Custom service host
  • Configuration

With attributes, we’d decorate all of our services with something like [StructureMapServiceBehavior] or something similar, so that WCF would know to attach our IServiceBehavior to the ServiceDescription behaviors.  Attributes are okay, but again, we’d have to put something on all of our services.  Since the whole point of this exercise was to reduce the footprint, let’s go the custom service host route.

As for configuration, I can’t stand XML, so let’s just pretend that one doesn’t exist.

A custom service host

By going the custom ServiceHost route, we’ll just need to plug in to the right event to add our custom service behavior to the mix.  Here’s what we wind up creating:

public class StructureMapServiceHost : ServiceHost
{
    public StructureMapServiceHost()
    {
    }

    public StructureMapServiceHost(Type serviceType, params Uri[] baseAddresses)
        : base(serviceType, baseAddresses)
    {
    }

    protected override void OnOpening()
    {
        Description.Behaviors.Add(new StructureMapServiceBehavior());
        base.OnOpening();
    }
}

Nothing too exciting, we just add our custom service behavior to the ServiceDescription Behaviors collection, right before the service host is opened in the OnOpening method.

Next, to plug in our custom service host, we’ll need a custom service host factory:

public class StructureMapServiceHostFactory : ServiceHostFactory
{
    public StructureMapServiceHostFactory()
    {
        StructureMapConfiguration
            .ScanAssemblies()
            .IncludeTheCallingAssembly()
            .With<DefaultConventionScanner>();
    }

    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        return new StructureMapServiceHost(serviceType, baseAddresses);
    }
}

The overridden method makes sense, override the CreateServiceHost to…create our custom service host.  However, there’s quite a bit going on in the constructor.

At this point, it will start to matter how you’d like StructureMap to be configured:

  • Attributes
  • XML config
  • Code

I’m liking the code way, as we don’t have to have crazy XML or a bunch of attributes everywhere.  Which way you configure is up to you, but if you go the code route, you’ll need to put the StructureMap configuration in the constructor here.

Finally, we’ll need to configure our WCF service to use this service host factory.  I’m using IIS to host, so I’ll just need to change my .svc file:

<%@ ServiceHost Language="C#" Debug="true" 
    Service="SMExample.Wcf.CustomerSearchService" 
    Factory="SMExample.Wcf.StructureMapServiceHostFactory" %>

Other hosting solutions will use different ways of using the custom service host factory.

Recap

We wanted to use StructureMap with WCF, but this initially presented us with some problems.  WCF requires a no-args constructor, which means we’ll have to use a lot of ObjectFactory.GetInstance calls on every service implementation.

Instead, we can use WCF extension points to use StructureMap to create the service for us, without needing that messy constructor.  We created two extensions:

  • IInstanceProvider
  • IServiceBehavior

Once we had our custom instance provider and service behavior, we needed to decide how our custom service behavior would get attached to our service.  We chose the custom service host route, which meant two more custom WCF implementations:

  • ServiceHost
  • ServiceHostFactory

The ServiceHostFactory implementation forced us to decide how our dependencies should be configured in StructureMap, and we went the code route.  Finally, we configured our service host in IIS to use the right service host factory.

With all this in place, none of our service classes nor their implementations need to know anything about StructureMap, allowing their design to grow and change outside of WCF construction concerns.  We also reduced the footprint of StructureMap in our application, where we now had exactly one call to the StructureMap infrastructure, ObjectFactory.GetInstance.  With this one place StructureMap is used, it will be easier to swap out IoC container implementations (just kidding, Jeremy).

Arrange Act Assert and BDD specifications