views:

458

answers:

3

I have WCF services structured like suggested by Miguel Castro. This means that I have set everything up manually, and have a console application hosting my services using ServiceHost objects.

I want to keep my service classes thin, and they are currently just passing on calls to behavior classes. My problem now is unit testing the service classes. I want to inject something to the classes as a constructor parameter such that I can mock this away and write proper isolated unit tests. The ServiceHost class doesn't seem to accept arguments, so my question is how I can inject data to the service classes - or can't I?

+5  A: 

WCF supports Constructor Injection, but you have to jump through a few hoops to get there. The key lies in writing a custom ServiceHostFactory. While that, too, must have a default constructor, you can use it to wire up all the correct behaviors.

As an example, I recently wrote one that uses Castle Windsor to wire up dependencies for the service implementation. The implementation of CreateServiceHost simply does this:

return new WindsorServiceHost(this.container, serviceType, baseAddresses);

where this.container is a configured IWindsorContainer.

WindsorServiceHost looks like this:

public class WindsorServiceHost : ServiceHost
{
    public WindsorServiceHost(IWindsorContainer container, Type serviceType, params Uri[] baseAddresses)
        : base(serviceType, baseAddresses)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        foreach (var cd in this.ImplementedContracts.Values)
        {
            cd.Behaviors.Add(new WindsorInstanceProvider(container));
        }
    }
}

and WindsorInstanceProvider looks like this:

public class WindsorInstanceProvider : IInstanceProvider, IContractBehavior
{
    private readonly IWindsorContainer container;

    public WindsorInstanceProvider(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        this.container = container;
    }

    #region IInstanceProvider Members

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

    public object GetInstance(InstanceContext instanceContext)
    {
        var serviceType = instanceContext.Host.Description.ServiceType;
        return this.container.Resolve(serviceType);
    }

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
        this.container.Release(instance);
    }

    #endregion

    #region IContractBehavior Members

    public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
    }

    public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
    {
        dispatchRuntime.InstanceProvider = this;
    }

    public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
    {
    }

    #endregion
}

This may look like a lot, but notice that it's reusable, general-purpose code that has a rather low cyclomatic complexity.

You can follow the same coding idiom to implement Dependency Injection with another DI Container or by using Poor Man's DI.

Here's an older writeup of this idiom that uses Poor Man's DI.

Mark Seemann
Thx for your reply. Looks promissing! Will digg into it tomorrow!
stiank81
Very helpful answer.
Joel Cunningham
+1  A: 

did you configure your service as a Singleton? i found out that IInstanceProvider implementations may be problematic when using a DI container to create the service instances.

Joachim Kerschbaumer
+2  A: 

If you were using Castle Windsor, it has a great WCF integration facility that lets you do this, and a lot more very easily.

Krzysztof Koźmic