107 votes

Comment puis-je passer les valeurs du constructeur, sur mon service wcf?

Je tiens à transmettre des valeurs dans le constructeur de la classe qui implémente mon service.

Cependant ServiceHost me laisse uniquement passer le nom du type à créer, ne pas quels sont les arguments à transmettre à ses contrstructor.

Je voudrais être en mesure de passer dans une usine qui crée mon objet de service.

Ce que j'ai trouvé jusqu'à présent:

127voto

Mark Seemann Points 102767

Vous aurez besoin de mettre en œuvre une combinaison personnalisée ServiceHostFactory, ServiceHost et IInstanceProvider.

Compte tenu d'un service avec cette signature du constructeur:

public MyService(IDependency dep)

Voici un exemple qui peut tourner jusqu'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 dans votre MyService.svc fichier, ou l'utiliser directement dans le code pour l'auto-hébergement scénarios.

Vous pouvez facilement généraliser cette approche, et en fait, certaines DI Conteneurs ont déjà fait pour vous (ndlr: Windsor mfa).

15voto

kerim Points 124

Vous pouvez simplement créer et d'instance de votre Service et de l'instance de l' ServiceHost objet. La seule chose que vous avez à faire est d'ajouter un [ServiceBehaviour] attribut de votre service et de marquer tous les objets retournés avec [DataContract] d'attribut.

Voici une maquette:

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

et l'utilisation:

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

host.Open();

J'espère que cela va rendre la vie plus facile pour quelqu'un.

11voto

dalo Points 348

Marque de la réponse avec l' IInstanceProvider est correct.

Au lieu d'utiliser la coutume ServiceHostFactory vous pouvez également utiliser un attribut personnalisé (disons MyInstanceProviderBehaviorAttribute). Calculer à partir du Attribute, qu'il applique IServiceBehavior et de mettre en œuvre l' IServiceBehavior.ApplyDispatchBehavior méthode

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

Ensuite, appliquez l'attribut à votre service de la mise en œuvre de la classe

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

La troisième option: vous pouvez également appliquer un comportement de service en utilisant le fichier de configuration.

5voto

McGarnagle Points 56802

J'ai travaillé à partir de la Marque de réponse, mais (pour mon scénario au moins), c'était inutilement complexe. L'un de l' ServiceHost constructeurs accepte une instance du service, vous pouvez passer directement à partir de l' ServiceHostFactory mise en œuvre.

À dos de Marque de l'exemple, il devrait ressembler à ceci:

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

0voto

Ron Deijkers Points 297

Nous avons été confrontés à ce même problème et l'ont résolu de la manière suivante. C'est une solution simple.

Dans Visual Studio, il suffit de créer une normale de service WCF application et de le supprimer de l'interface. Quitter la .cs fichier en place (il suffit de renommer) et ouvert que le cs fichier et remplacer le nom de l'interface avec l'original de votre nom de la classe qui implémente la logique de service (de cette façon, la classe de service utilise l'héritage et remplace votre réelle mise en œuvre). Ajouter un constructeur par défaut qui appelle la base de constructeurs de la classe, comme ceci:

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

Le MyService de la classe de base est la mise en œuvre effective du service. Cette classe de base ne devrait pas avoir un constructeur sans paramètre, mais seulement les constructeurs avec paramètres qui acceptent les dépendances.

Le service doit utiliser cette classe, au lieu de l'original MyService.

C'est une solution simple et fonctionne comme un charme :-D

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X