Подтвердить что ты не робот

Использование инъекции зависимостей Unity с услугами WCF

У меня есть следующие результаты после некоторых исследований по другим вопросам:

MyServiceHost:

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 имеет синтаксическую ошибку из-за первого аргумента, ожидающего строку Constructor, а не тип. Который я не понимаю, так как он принимает параметр Type. В дополнение к этому я не понимаю, где я должен отображать IFakeDAL для конкретного класса. Могу ли я сделать это в файле app.config или я должен зарегистрировать это где-то еще?

4b9b3361

Ответ 1

ServiceHostFactory предназначен для размещения в IIS. В самостоятельном хостинге вы должны напрямую использовать свой производный ServiceHost. Здесь у вас есть весь пример, включая конфигурацию 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 ожидает массив экземпляров Uri, поэтому попробуйте это вместо:

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

Вы можете сопоставлять интерфейсы с типами в XML или коде, но я бы рекомендовал код, так как XML слишком сильно накладные расходы на обслуживание.

Метод Main является отличным корнем композиции, но если вы хотите настроить контейнер на этом уровне, вам нужно будет передайте его от метода Main до MyServiceHostFactory - это прекрасно, когда вы размещаете службу в консольном приложении, но не будете работать, если вы хотите разместить ее в IIS, где MyServiceHostFactory должен быть корнем композиции, поскольку для IIS требуется конструктор по умолчанию.