Jak przekazać wartości do konstruktora w mojej usłudze wcf?

Chciałbym przekazać wartości do konstruktora na klasie, która implementuje moją usługę.

Jednak ServiceHost pozwala mi przekazać tylko nazwę typu do utworzenia, a nie jakie argumenty przekazać do jego contrstructor.

Chciałbym móc przejść w fabryce, która tworzy mój obiekt serwisowy.

Co do tej pory znalazłem:

Author: Elias Platek, 2010-03-16

8 answers

Musisz zaimplementować kombinację niestandardowych ServiceHostFactory, ServiceHost i IInstanceProvider.

Podana usługa z tym podpisem konstruktora:

public MyService(IDependency dep)

Oto przykład, który może spin up 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
}

Zarejestruj MyServiceHostFactory w swoim MyService.plik svc lub użyj MyServiceHost bezpośrednio w kodzie dla scenariuszy self-hostingu.

Można łatwo uogólnić to podejście, a w rzeczywistości niektóre kontenery DI już to za Ciebie zrobili (cue: Windsor ' s WCF Facility).

 111
Author: Mark Seemann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-02 18:23:59

Możesz po prostu utworzyć i instancję swojego Service i przekazać tę instancję do obiektu ServiceHost. Jedyne, co musisz zrobić, to dodać atrybut [ServiceBehaviour] dla swojej usługi i oznaczyć wszystkie zwrócone obiekty atrybutem [DataContract].

Oto makieta:

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

I użycie:

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

host.Open();
Mam nadzieję, że to ułatwi komuś życie.
 12
Author: kerim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-11 09:48:24

Odpowiedź marka z {[2] } jest poprawna.

Zamiast używać niestandardowego ServiceHostFactory można również użyć niestandardowego atrybutu (powiedzmy MyInstanceProviderBehaviorAttribute). Wywołaj ją z Attribute, Wykonaj implementację IServiceBehavior i zaimplementuj metodę IServiceBehavior.ApplyDispatchBehavior Jak

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

Następnie zastosuj atrybut do klasy implementacji usługi

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

Trzecia opcja: możesz również zastosować zachowanie usługi za pomocą pliku konfiguracyjnego.

 10
Author: dalo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-27 18:04:48

Pracowałem na podstawie odpowiedzi marka, ale (przynajmniej dla mojego scenariusza), było to niepotrzebnie skomplikowane. Jeden z konstruktorów ServiceHost akceptuje instancję usługi, którą można przekazać bezpośrednio z implementacji ServiceHostFactory.

Na przykładzie Marka wyglądałoby to tak:

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
Author: McGarnagle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-10 23:52:26

Pieprzyć to ... zmieszałem wzorce dependency injection i service locator (ale głównie jest to wciąż dependency injection i nawet odbywa się w konstruktorze, co oznacza, że możesz mieć stan tylko do odczytu).

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

Zależności usługi są wyraźnie określone w umowie jej zagnieżdżonej klasy Dependencies. Jeśli używasz kontenera IoC (takiego, który nie naprawia za Ciebie bałaganu WCF), możesz skonfigurować go tak, aby utworzyć instancję Dependencies zamiast usługi. Tędy. otrzymujesz ciepłe, rozmyte uczucie, które daje twój pojemnik, a jednocześnie nie musisz przeskakiwać przez zbyt wiele obręczy nałożonych przez WCF.

Nie zamierzam tracić snu przez to podejście. Nikt inny nie powinien. W końcu you ' re IoC container to duży, tłusty, statyczny zbiór delegatów, który tworzy rzeczy dla Ciebie. Po co dodawać jeszcze jedną?
 3
Author: Ronnie Overby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-24 15:10:33

To było bardzo pomocne rozwiązanie-szczególnie dla kogoś, kto jest początkującym programistą WCF. Chciałem opublikować małą wskazówkę dla użytkowników, którzy mogą używać tego w usłudze hostowanej przez IIS. MyServiceHost musi dziedziczyć WebServiceHost , a nie tylko ServiceHost.

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

Spowoduje to utworzenie wszystkich niezbędnych wiązań itp. dla punktów końcowych w IIS.

 0
Author: Eric Dieckman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-10 15:37:49

Używam zmiennych statycznych mojego typu. Nie wiem czy to najlepszy sposób, ale u mnie działa:

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

Kiedy uruchamiam host serwisu wykonuję następujące czynności:

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

    ...

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

    ....
}
 -1
Author: Boris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-08 08:40:51

Mieliśmy do czynienia z tym samym problemem i rozwiązaliśmy go w następujący sposób. Jest to proste rozwiązanie.

W Visual Studio wystarczy utworzyć zwykłą aplikację usługową WCF i usunąć jej interfejs. Zostawićplik cs na miejscu (po prostu zmień jego nazwę) i otwórz ten plik cs i zamień nazwę interfejsu na oryginalną nazwę klasy, która implementuje logikę usługi (w ten sposób Klasa service używa dziedziczenia i zastępuje rzeczywistą implementację). Dodaj domyślny konstruktor, który wywołuje konstruktory klasy bazowej, w następujący sposób:

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

Klasa bazowa MyService jest rzeczywistą implementacją usługi. Ta klasa bazowa nie powinna mieć konstruktora bez parametru, ale tylko konstruktory z parametrami, które akceptują zależności.

Usługa powinna używać tej klasy zamiast oryginalnego MyService.

To proste rozwiązanie i działa jak czar :-D

 -1
Author: Ron Deijkers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-24 12:03:48