stiank81
stiank81

Reputation: 25716

Injecting data to a WCF service

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?

Upvotes: 20

Views: 15443

Answers (4)

Michael Freidgeim
Michael Freidgeim

Reputation: 28491

The article Hosting a Mock as a WCF service contains a static method that will generate a WCF service host based on the object that is passed into the method with a single endpoint.

The method was also posted in the answer for Recommended patterns for unit testing web services.

Example of usage calls NSubstitute, but other mocking freameworks can be used.

Upvotes: 0

Krzysztof Kozmic
Krzysztof Kozmic

Reputation: 27384

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

Upvotes: 5

Joachim Kerschbaumer
Joachim Kerschbaumer

Reputation: 9881

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.

Upvotes: 1

Mark Seemann
Mark Seemann

Reputation: 233327

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.

Upvotes: 33

Related Questions