【问题标题】:Using Unity Dependency Injection with WCF services将 Unity 依赖注入与 WCF 服务一起使用
【发布时间】:2011-09-08 22:58:38
【问题描述】:

在对其他问题做了一些研究后,我有以下几点:

我的服务主机:

public class MyServiceHost : ServiceHost
{
    public MyServiceHost(IUnityContainer 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 DependencyInjectionInstanceProvider(container));
        }
    }
}

DependencyInjectionInstanceProvider:

public class DependencyInjectionInstanceProvider : IInstanceProvider, IContractBehavior 
{     
    private readonly IUnityContainer container;      
    public DependencyInjectionInstanceProvider(IUnityContainer 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.Teardown(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 

} 

MyServiceHostFactory:

    public class MyServiceHostFactory : ServiceHostFactory
{
    private readonly IUnityContainer container;     
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) 
    { 
        return new MyServiceHost(this.container, serviceType, baseAddresses); 
    }
}

尝试构造函数注入的电子邮件服务:

public class EmailValidator : IEmailValidator
{
    private IFakeDAL fakeDAL;

    public EmailValidator(IFakeDAL fakeDAL)
    {
        this.fakeDAL = fakeDAL;
    }

    public bool ValidateAddress(string emailAddress)
    {
        Console.WriteLine("Validating: {0}", emailAddress);

        string pattern = @"^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@(([0-9a-zA-Z])+([-\w]*[0-9a-zA-Z])*\.)+[a-zA-Z]{2,9})$";
        return Regex.IsMatch(emailAddress, pattern);
    }
}

我的控制台主机启动服务:

static void Main(string[] args)
    {
        Type serviceType = typeof(EmailValidator);
        Uri serviceUri = new Uri("http://localhost:8080/");

        MyServiceHostFactory shf = new MyServiceHostFactory();
        ServiceHost host = shf.CreateServiceHost(serviceType, serviceUri);
        //ServiceHost host = new ServiceHost(serviceType, serviceUri);
        host.Open();

我的问题在于控制台主机逻辑。由于第一个参数需要构造函数字符串而不是类型,因此 CreateServiceHost 调用存在语法错误。我不明白,因为它确实接受 Type 参数。除此之外,我不明白应该将 IFakeDAL 映射到具体类的位置。我可以在 app.config 文件中执行此操作,还是应该在其他地方注册?

【问题讨论】:

    标签: wcf dependency-injection unity-container


    【解决方案1】:

    ServiceHostFactory 用于在 IIS 中托管。在自我托管中,您应该直接使用派生的ServiceHostHere 你有完整的例子,包括 Unity 配置。

    【讨论】:

    • 谢谢。它看起来是一个很好的例子。我实际上可能会弄清楚这一点。 :)
    【解决方案2】:

    我在我的 Windows 服务中使用以下类来创建 WCF 服务并使用统一向它注入依赖项。

    UnityInstanceProvider:

    internal class UnityInstanceProvider : IInstanceProvider {
    
        private readonly IUnityContainer container;
        private readonly Type contractType;
    
        public UnityInstanceProvider(IUnityContainer container, Type contractType) {
            this.container = container;
            this.contractType = contractType;
        }
    
        public object GetInstance(InstanceContext instanceContext) {
            return GetInstance(instanceContext, null);
        }
    
        public object GetInstance(InstanceContext instanceContext, Message message) {
            return container.Resolve(contractType);
        }
    
        public void ReleaseInstance(InstanceContext instanceContext, object instance) {
            container.Teardown(instance);
        }
    }
    

    UnityServiceBehavior:

    public class UnityServiceBehavior : IServiceBehavior {
    
        private readonly IUnityContainer container;
    
        public UnityServiceBehavior(IUnityContainer container) {
            this.container = container;
        }
    
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {
        }
    
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) {
        }
    
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {
            foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers) {
                foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints) {
                    if (endpointDispatcher.ContractName != "IMetadataExchange") {
                        string contractName = endpointDispatcher.ContractName;
                        ServiceEndpoint serviceEndpoint = serviceDescription.Endpoints.FirstOrDefault(e => e.Contract.Name == contractName);
                        endpointDispatcher.DispatchRuntime.InstanceProvider = new UnityInstanceProvider(this.container, serviceEndpoint.Contract.ContractType);
                    }
                }
            }
        }
    }
    

    UnityServiceHost:

    public class UnityServiceHost : ServiceHost {
    
        private IUnityContainer unityContainer;
    
        public UnityServiceHost(IUnityContainer unityContainer, Type serviceType)
            : base(serviceType) {
            this.unityContainer = unityContainer;
        }
    
        protected override void OnOpening() {
            base.OnOpening();
    
            if (this.Description.Behaviors.Find<UnityServiceBehavior>() == null) {
                this.Description.Behaviors.Add(new UnityServiceBehavior(this.unityContainer));
            }
        }
    }
    

    使用这些类,您可以执行以下操作(服务的配置在 .config 中完成):

    UnityContainer container = new UnityContainer();
    UnityServiceHost serviceHost = new UnityServiceHost(container, typeof("Type of Service to host"));
    serviceHost.Open();
    

    【讨论】:

    • 所以你正在映射到配置文件中的具体类?
    • 您介意在配置文件中显示您的服务配置吗?
    【解决方案3】:

    CreateServiceHost method 需要 Uri 实例的数组,因此请尝试以下操作:

    ServiceHost host = shf.CreateServiceHost(serviceType, new[] { serviceUri });
    

    您可以将接口映射到 XML 或代码中的类型,但我建议使用代码,因为 XML 的维护开销太高。

    Main 方法是一个很好的Composition Root,但是如果你想在那个级别配置容器,你需要将它从Main 方法传递给MyServiceHostFactory - 这是非常好的您将服务托管在控制台应用程序中,但如果您想将其托管在 IIS 中,MyServiceHostFactoryshould be the Composition Root, since IIS requires a default constructor 将无法工作。

    【讨论】:

    • +1 评论组合根应该在哪里。顺便说一句,喜欢马克这本书。
    猜你喜欢
    • 1970-01-01
    • 2014-02-10
    • 2019-04-13
    • 1970-01-01
    • 1970-01-01
    • 2021-02-09
    • 1970-01-01
    • 1970-01-01
    • 2011-09-11
    相关资源
    最近更新 更多