如何将值传递给我的wcf服务上的构造函数?

Ian*_*ose 102 wcf dependency-injection factory-pattern

我想将值传递给实现我的服务的类的构造函数.

但是,ServiceHost只允许我传递要创建的类型的名称,而不是传递给其构造函数的参数.

我希望能够传入一个创建我的服务对象的工厂.

到目前为止我发现了什么:

Mar*_*ann 119

你需要实现自定义的组合ServiceHostFactory,ServiceHostIInstanceProvider.

给定具有此构造函数签名的服务:

public MyService(IDependency dep)
Run Code Online (Sandbox Code Playgroud)

这是一个可以启动MyService的示例:

public class MyServiceHostFactory : ServiceHostFactory
{
    private readonly IDependency dep;

    public MyServiceHostFactory()
    {
        this.dep = new MyClass();
    }

    protected override ServiceHost CreateServiceHost(Type serviceType,
        Uri[] baseAddresses)
    {
        return new MyServiceHost(this.dep, serviceType, baseAddresses);
    }
}

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

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

public class MyInstanceProvider : IInstanceProvider, IContractBehavior
{
    private readonly IDependency dep;

    public MyInstanceProvider(IDependency dep)
    {
        if (dep == null)
        {
            throw new ArgumentNullException("dep");
        }

        this.dep = dep;
    }

    #region IInstanceProvider Members

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

    public object GetInstance(InstanceContext instanceContext)
    {
        return new MyService(this.dep);
    }

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
        var disposable = instance as IDisposable;
        if (disposable != null)
        {
            disposable.Dispose();
        }
    }

    #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
}
Run Code Online (Sandbox Code Playgroud)

在MyService.svc文件中注册MyServiceHostFactory,或直接在代码中使用MyServiceHost进行自托管方案.

您可以轻松地概括这种方法,事实上一些DI容器已经为您完成了这个(提示:Windsor的WCF工厂).

  • 我怎样才能将它用于自托管?调用CreateServiceHost后收到异常.我只能调用protected方法public override ServiceHostBase CreateServiceHost(string constructorString,Uri [] baseAddresses); 例外是异常消息:无法在当前托管环境中调用"ServiceHostFactory.CreateServiceHost".此API要求将调用应用程序托管在IIS或WAS中. (5认同)
  • @Guy我有样品问题.因为函数是`protected`我不能自己从Main()调用它 (2认同)
  • @MarkSeemann我只是想知道,你为什么要把'dep`注入每个*Contract的*InstanceProvider.你可以这样做:`ImplementedContracts.Values.First(c => c.Name =="IMyService").ContractBehaviors.Add(new MyInstanceProvider(dep));```IMyService`是你的MyService的契约接口(IDependency) DEP)`.因此,只将"IDependency"注入到实际需要它的InstanceProvider中. (2认同)

ker*_*rim 14

您可以简单地创建和实例化您的Service实例并将该实例传递给ServiceHost对象.您唯一需要做的就是为服务添加[ServiceBehaviour]属性并使用[DataContract]属性标记所有返回的对象.

这是一个模拟:

namespace Service
{
    [ServiceContract]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class MyService
    {
        private readonly IDependency _dep;

        public MyService(IDependency dep)
        {
            _dep = dep;
        }

        public MyDataObject GetData()
        {
            return _dep.GetData();
        }
    }

    [DataContract]
    public class MyDataObject
    {
        public MyDataObject(string name)
        {
            Name = name;
        }

        public string Name { get; private set; }
    }

    public interface IDependency
    {
        MyDataObject GetData();
    }
}
Run Code Online (Sandbox Code Playgroud)

和用法:

var dep = new Dependecy();
var myService = new MyService(dep);
var host = new ServiceHost(myService);

host.Open();
Run Code Online (Sandbox Code Playgroud)

我希望这会让某人的生活更轻松.

  • 这仅适用于单身人士(如`InstanceContextMode.Single`所示). (5认同)

dal*_*alo 11

马克的回答IInstanceProvider是正确的.

您也可以使用自定义属性(例如MyInstanceProviderBehaviorAttribute),而不是使用自定义ServiceHostFactory .从中派生出来Attribute,使其实现IServiceBehavior并实现IServiceBehavior.ApplyDispatchBehavior类似的方法

// YourInstanceProvider implements IInstanceProvider
var instanceProvider = new YourInstanceProvider(<yourargs>);

foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
    foreach (var epDispatcher in dispatcher.Endpoints)
    {
        // this registers your custom IInstanceProvider
        epDispatcher.DispatchRuntime.InstanceProvider = instanceProvider;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,将该属性应用于您的服务实现类

[ServiceBehavior]
[MyInstanceProviderBehavior(<params as you want>)]
public class MyService : IMyContract
Run Code Online (Sandbox Code Playgroud)

第三个选项:您还可以使用配置文件应用服务行为.

  • 从技术上讲,这也看起来像一个解决方案,但是通过这种方法,您可以将IInstanceProvider与服务紧密结合. (2认同)
  • 只是第二种选择,没有评估哪些更好.我已经多次使用自定义ServiceHostFactory(特别是当你想注册几个行为时). (2认同)

McG*_*gle 5

我从马克的答案开始,但(至少在我的情况下),它是不必要的复杂.其中一个ServiceHost构造函数接受服务的实例,您可以直接从ServiceHostFactory实现传入该实例.

为了搭载Mark的例子,它看起来像这样:

public class MyServiceHostFactory : ServiceHostFactory
{
    private readonly IDependency _dep;

    public MyServiceHostFactory()
    {
        _dep = new MyClass();
    }

    protected override ServiceHost CreateServiceHost(Type serviceType,
        Uri[] baseAddresses)
    {
        var instance = new MyService(_dep);
        return new MyServiceHost(instance, serviceType, baseAddresses);
    }
}

public class MyServiceHost : ServiceHost
{
    public MyServiceHost(MyService instance, Type serviceType, params Uri[] baseAddresses)
        : base(instance, baseAddresses)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您的服务和所有注入的依赖项都是线程安全的,那么这将有效.ServiceHost构造函数的特定重载实质上禁用了WCF的生命周期管理.相反,你说'所有*并发请求将由`instance`处理.这可能会也可能不会影响绩效.如果您希望能够处理并发请求,那么*整个*对象图必须是线程安全的,否则您将得到不确定的,不正确的行为.如果你能保证线程安全,我的解决方案确实是不必要的复杂.如果您无法保证,我的解决方案是必需的. (12认同)