it-swarm.com.de

Wie übergebe ich Werte an den Konstruktor meines wcf-Dienstes?

Ich möchte Werte an den Konstruktor der Klasse übergeben, die meinen Service implementiert.

Mit ServiceHost kann ich jedoch nur den Namen des zu erstellenden Typs übergeben, nicht die Argumente, die an den Konstruktor übergeben werden sollen.

Ich möchte in der Lage sein, eine Fabrik zu übergeben, die mein Serviceobjekt erstellt.

Was ich bisher gefunden habe:

102
Ian Ringrose

Sie müssen eine Kombination aus benutzerdefiniertem ServiceHostFactory, ServiceHost und IInstanceProvider implementieren.

Einen Service mit dieser Konstruktorsignatur erhalten:

public MyService(IDependency dep)

Hier ist ein Beispiel, das MyService starten kann:

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
}

Registrieren Sie MyServiceHostFactory in Ihrer MyService.svc-Datei oder verwenden Sie MyServiceHost direkt im Code für Self-Hosting-Szenarien.

Sie können diesen Ansatz leicht verallgemeinern, und tatsächlich haben einige DI-Container dies bereits für Sie getan (Stichwort: Windsor's WCF Facility).

119
Mark Seemann

Sie können einfach eine Instanz Ihres Service erstellen und an das ServiceHost -Objekt übergeben. Das einzige, was Sie tun müssen, ist das Hinzufügen eines [ServiceBehaviour] Attribut für Ihren Dienst und markieren Sie alle zurückgegebenen Objekte mit [DataContract] Attribut.

Hier ist ein Modell:

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

und die Verwendung:

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

Host.Open();

Ich hoffe, das wird jemandem das Leben leichter machen.

13
kerim

Marks Antwort mit dem IInstanceProvider ist richtig.

Anstelle der benutzerdefinierten ServiceHostFactory können Sie auch ein benutzerdefiniertes Attribut verwenden (z. B. MyInstanceProviderBehaviorAttribute). Leiten Sie es von Attribute ab, lassen Sie es IServiceBehavior implementieren und implementieren Sie das IServiceBehavior.ApplyDispatchBehavior Methode wie

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

Wenden Sie dann das Attribut auf Ihre Serviceimplementierungsklasse an

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

Die dritte Option: Sie können ein Dienstverhalten auch mithilfe der Konfigurationsdatei anwenden.

11
dalo

Ich habe nach Marks Antwort gearbeitet, aber (zumindest für mein Szenario) war es unnötig komplex. Einer der Konstruktoren ServiceHost akzeptiert eine Instanz des Dienstes, die Sie direkt von der Implementierung ServiceHostFactory übergeben können.

Als Huckepack von Marks Beispiel würde es so aussehen:

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

Schrauben Sie es ... Ich habe die Muster für die Abhängigkeitsinjektion und den Service-Locator überblendet (aber meistens handelt es sich immer noch um die Abhängigkeitsinjektion, und sie findet sogar im Konstruktor statt, was bedeutet, dass Sie einen Nur-Lese-Status haben können).

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

Die Abhängigkeiten des Dienstes sind im Vertrag der geschachtelten Klasse Dependencies eindeutig angegeben. Wenn Sie einen IoC-Container verwenden (der das WCF-Problem nicht bereits behebt), können Sie ihn so konfigurieren, dass anstelle des Dienstes die Instanz Dependencies erstellt wird. Auf diese Weise erhalten Sie das warme, unscharfe Gefühl, das Ihnen Ihr Container vermittelt, ohne dass Sie durch zu viele Reifen springen müssen, die WCF auferlegt hat.

Ich werde bei dieser Herangehensweise nicht den Schlaf verlieren. Weder sollte jemand anderes. Schließlich ist Ihr IoC-Container eine große, fette, statische Sammlung von Delegierten, die Dinge für Sie erstellt. Was fügt man noch hinzu?

3
Ronnie Overby

Dies war eine sehr hilfreiche Lösung - insbesondere für jemanden, der ein neuer WCF-Codierer ist. Ich wollte einen kleinen Tipp für alle Benutzer veröffentlichen, die dies möglicherweise für einen von IIS gehosteten Dienst verwenden. MyServiceHost muss WebServiceHost erben, nicht nur ServiceHost.

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

Dadurch werden alle erforderlichen Bindungen usw. für Ihre Endpunkte in IIS erstellt.

0
Eric Dieckman

Wir standen vor dem gleichen Problem und haben es auf folgende Weise gelöst. Es ist eine einfache Lösung.

Erstellen Sie in Visual Studio einfach eine normale WCF-Dienstanwendung und entfernen Sie deren Schnittstelle. Lassen Sie die CS-Datei an Ort und Stelle (benennen Sie sie einfach um) und öffnen Sie diese CS-Datei und ersetzen Sie den Namen der Schnittstelle durch Ihren ursprünglichen Klassennamen, der die Dienstlogik implementiert (auf diese Weise verwendet die Dienstklasse die Vererbung und ersetzt Ihre tatsächliche Implementierung). Fügen Sie einen Standardkonstruktor hinzu, der die Konstruktoren der Basisklasse wie folgt aufruft:

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

Die MyService-Basisklasse ist die tatsächliche Implementierung des Dienstes. Diese Basisklasse sollte keinen parameterlosen Konstruktor haben, sondern nur Konstruktoren mit Parametern, die die Abhängigkeiten akzeptieren.

Der Dienst sollte diese Klasse anstelle des ursprünglichen MyService verwenden.

Es ist eine einfache Lösung und funktioniert wie ein Zauber :-D

0
Ron Deijkers