将数据注入WCF服务

时间:2010-01-11 15:06:25

标签: .net wcf unit-testing dependency-injection service

我的WCF服务结构与Miguel Castro建议的结构相同。这意味着我已手动设置所有内容,并使用ServiceHost对象托管我的服务的控制台应用程序。

我想保持我的服务类很薄,他们目前只是调用行为类。我现在的问题是单元测试服务类。我想向类中注入一些东西作为构造函数参数,这样我就可以模拟它并编写适当的隔离单元测试。 ServiceHost类似乎不接受参数,所以我的问题是如何将数据注入服务类 - 或者我不能?

4 个答案:

答案 0 :(得分:32)

WCF支持构造函数注入,但您必须跳过几个环节才能到达那里。关键在于编写自定义ServiceHostFactory。虽然这也必须有默认构造函数,但您可以使用它来连接所有正确的行为。

作为一个例子,我最近写了一篇使用Castle Windsor来连接服务实现的依赖项的文章。 CreateServiceHost的实现只是这样做:

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

其中this.container是已配置的IWindsorContainer。

WindsorServiceHost看起来像这样:

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));
        }
    }
}

和WindsorInstanceProvider看起来像这样:

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
}

这可能看起来很多,但请注意它是可重复使用的通用代码,具有相当低的圈复杂性。

您可以使用相同的编码习惯用另一个DI容器或使用穷人的DI来实现依赖注入。

这是使用穷人的DI的这个成语的older writeup

答案 1 :(得分:5)

如果您使用Castle Windsor,它有一个很棒的WCF集成工具,可以让您更轻松地完成此任务。

答案 2 :(得分:1)

您是否将服务配置为Singleton?我发现当使用DI容器创建服务实例时,IInstanceProvider实现可能会有问题。

答案 3 :(得分:0)

文章Hosting a Mock as a WCF service  包含一个静态方法,该方法将根据使用单个端点传递给方法的对象生成WCF服务主机。

该方法也发布在answerRecommended patterns for unit testing web services

使用示例调用NSubstitute,但可以使用其他模拟freameworks。