Pergunta

Gostaria de passar valores para o construtor da classe que implementa meu serviço.

No entanto, ServiceHost só me permite passar o nome do tipo a ser criado, não quais argumentos passar ao seu construtor.

Gostaria de poder passar em uma fábrica que crie meu objeto de serviço.

O que descobri até agora:

Foi útil?

Solução

Você precisará implementar uma combinação de personalizada ServiceHostFactory, ServiceHost e IInstanceProvider.

Dado um serviço com esta assinatura do construtor:

public MyService(IDependency dep)

Aqui está um exemplo que pode girar o 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
}

Registre myServiceHostFactory no seu arquivo myService.svc ou use o MyServiceHost diretamente no código para cenários de auto-hospedagem.

Você pode generalizar facilmente essa abordagem e, de fato, alguns contêineres de DI já fizeram isso para você (Cue: Windsor's WCF Facility).

Outras dicas

Você pode simplesmente criar e instância do seu Service e passar essa instância para o ServiceHost objeto. A única coisa que você precisa fazer é adicionar um [ServiceBehaviour] atributo para seu serviço e marque todos os objetos devolvidos com [DataContract] atributo.

Aqui está uma simulação:

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

E o uso:

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

host.Open();

Espero que isso facilite a vida para alguém.

Resposta de Mark com o IInstanceProvider está correto.

Em vez de usar o serviço de serviço personalizado, você também pode usar um atributo personalizado (digamos MyInstanceProviderBehaviorAttribute). Deriva de Attribute, faça com que seja implementado IServiceBehavior e implementar o IServiceBehavior.ApplyDispatchBehavior método como

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

Em seguida, aplique o atributo à sua classe de implementação de serviço

[ServiceBehavior]
[MyInstanceProviderBehavior(<params as you want>)]
public class MyService : IMyContract

A terceira opção: você também pode aplicar um comportamento de serviço usando o arquivo de configuração.

Eu trabalhei com a resposta de Mark, mas (pelo menos no meu cenário), era desnecessariamente complexo. Um dos ServiceHost Os construtores aceitam uma instância do serviço, que você pode passar diretamente do ServiceHostFactory implementação.

Para pegar carona no exemplo de Mark, seria assim:

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

Enfrente-o ... misturei os padrões de injeção de dependência e localizador de serviços (mas principalmente ainda é injeção de dependência e até ocorre no construtor, o que significa que você pode ter um estado somente leitura).

public class MyService : IMyService
{
    private readonly Dependencies _dependencies;

    // set this before creating service host. this can use your IOC container or whatever.
    // if you don't like the mutability shown here (IoC containers are usually immutable after being configured)
    // you can use some sort of write-once object
    // or more advanced approach like authenticated access
    public static Func<Dependencies> GetDependencies { get; set; }     
    public class Dependencies
    {
        // whatever your service needs here.
        public Thing1 Thing1 {get;}
        public Thing2 Thing2 {get;}

        public Dependencies(Thing1 thing1, Thing2 thing2)
        {
            Thing1 = thing1;
            Thing2 = thing2;
        }
    }

    public MyService ()
    {
        _dependencies = GetDependencies(); // this will blow up at run time in the exact same way your IoC container will if it hasn't been properly configured up front. NO DIFFERENCE
    }
}

As dependências do serviço são claramente especificadas no contrato de seu aninhado Dependencies classe. Se você estiver usando um contêiner do IOC (um que ainda não conserta a bagunça do WCF para você), você pode configurá -lo para criar o Dependencies instância em vez do serviço. Dessa forma, você tem a sensação quente e confusa que seu contêiner lhe dá, além de não ter que pular por muitos aros impostos pelo WCF.

Não vou perder nenhum sono por causa dessa abordagem. Nem mais ninguém deveria. Afinal, seu contêiner do COI é uma coleção grande, gorda e estática de delegados que cria coisas para você. O que está adicionando mais um?

Estávamos enfrentando o mesmo problema e o resolvemos da seguinte maneira. É uma solução simples.

No Visual Studio, basta criar um aplicativo de serviço WCF normal e remover sua interface. Deixe o arquivo .CS no lugar (apenas renomeie -o) e abra esse arquivo CS e substitua o nome da interface pelo nome da classe original que implementa a lógica de serviço (dessa maneira, a classe de serviço usa a herança e substitui sua implementação real). Adicione um construtor padrão que chama os construtores da classe base, como este:

public class Service1 : MyLogicNamespace.MyService
{
    public Service1() : base(new MyDependency1(), new MyDependency2()) {}
}

A classe base do MyService é a implementação real do serviço. Esta classe base não deve ter um construtor sem parâmetros, mas apenas construtores com parâmetros que aceitam as dependências.

O serviço deve usar esta classe em vez do MyService original.

É uma solução simples e funciona como um charme: d

Esta foi uma solução muito útil - especialmente para alguém que é um codificador do WCF iniciante. Eu queria postar uma pequena dica para todos os usuários que poderiam estar usando isso para um serviço hospedado no IIS. MyServiceHost precisa herdar WebServiceHost, não apenas o serviço.

public class MyServiceHost : WebServiceHost
{
    public MyServiceHost(MyService instance, Type serviceType, params Uri[] baseAddresses)
        : base(instance, baseAddresses)
    {
    }
}

Isso criará todas as ligações necessárias, etc. para seus pontos de extremidade no IIS.

Eu uso variáveis ​​estáticas do meu tipo.Não tenho certeza se esta é a melhor maneira, mas funciona para mim:

public class MyServer
{   
    public static string CustomerDisplayName;
    ...
}

Quando instancio o host de serviço, faço o seguinte:

protected override void OnStart(string[] args)
{
    MyServer.CustomerDisplayName = "Test customer";

    ...

    selfHost = new ServiceHost(typeof(MyServer), baseAddress);

    ....
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top