Hoe geef ik waarden door aan de constructor van mijn wcf-service?

Ik wil graag waarden doorgeven aan de constructor van de klasse die mijn service implementeert.

ServiceHost laat me echter alleen de naam doorgeven van het type dat moet worden gemaakt, niet welke argumenten ik moet doorgeven aan de constructeur.

Ik zou graag een fabriek willen passeren die mijn serviceobject maakt.

Wat ik tot nu toe heb gevonden:


Antwoord 1, autoriteit 100%

U moet een combinatie van aangepaste ServiceHostFactory, ServiceHosten IInstanceProviderimplementeren.

Een service gegeven met deze constructorhandtekening:

public MyService(IDependency dep)

Hier is een voorbeeld dat MyService kan laten draaien:

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
}

Registreer MyServiceHostFactory in uw MyService.svc-bestand of gebruik MyServiceHost rechtstreeks in code voor scenario’s voor zelfhosting.

Je kunt deze benadering gemakkelijk veralgemenen, en in feite hebben sommige DI-containers dit al voor je gedaan (cue: Windsor’s WCF Facility).


Antwoord 2, autoriteit 11%

U kunt eenvoudig een instantie van uw Servicemaken en die instantie doorgeven aan het ServiceHost-object. Het enige dat u hoeft te doen is een [ServiceBehaviour]-kenmerk voor uw service toe te voegen en alle geretourneerde objecten te markeren met het [DataContract]-kenmerk.

Hier is een mock-up:

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

en het gebruik:

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

Ik hoop dat dit iemand het leven gemakkelijker zal maken.


Antwoord 3, autoriteit 9%

Mark’s antwoord met de IInstanceProvideris correct.

In plaats van de aangepaste ServiceHostFactory te gebruiken, kunt u ook een aangepast kenmerk gebruiken (bijvoorbeeld MyInstanceProviderBehaviorAttribute). Leid het af van Attribute, laat het IServiceBehaviorimplementeren en implementeer de IServiceBehavior.ApplyDispatchBehavior-methode zoals

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

Pas vervolgens het kenmerk toe op uw service-implementatieklasse

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

De derde optie: u kunt ook een servicegedrag toepassen met behulp van het configuratiebestand.


Antwoord 4, autoriteit 4%

Ik ging uit van Marks antwoord, maar (althans voor mijn scenario) was het nodeloos ingewikkeld. Een van de ServiceHost-constructors accepteert een instantie van de service, die u rechtstreeks vanuit de ServiceHostFactory-implementatie kunt doorgeven.

Om mee te liften op Marks voorbeeld, ziet het er als volgt uit:

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

Antwoord 5, autoriteit 3%

Schroef het… Ik heb de afhankelijkheidsinjectie en servicelocatorpatronen gemengd (maar meestal is het nog steeds afhankelijkheidsinjectie en het vindt zelfs plaats in de constructor, wat betekent dat u een alleen-lezen-status kunt hebben).

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

De afhankelijkheden van de dienst worden duidelijk gespecificeerd in het contract van het is genest Dependenciesklasse. Als u gebruik maakt van een IOC container (een die niet al het WCF puinhoop is opgelost voor u), kun je het instellen van de Dependenciesbijvoorbeeld in plaats van de dienst te maken. Op deze manier kunt de warme fuzzy gevoel dat uw container geeft u, terwijl ook niet hoeft te springen door te veel hoepels opgelegd door WCF te krijgen.

Ik ben niet van plan om wakker liggen deze aanpak. Evenmin zou iemand anders. Immers, je bent IoC container is een grote, dikke, statische verzameling van de afgevaardigden die dingen voor u creëert. Wat is het toevoegen van een meer?

Other episodes