Как передать значения конструктору в моем сервисе wcf? - PullRequest
102 голосов
/ 16 марта 2010

Я хотел бы передать значения в конструктор класса, который реализует мой сервис.

Однако ServiceHost позволяет мне передавать только имя создаваемого типа, а не аргументы, передаваемые его конструктору.

Я бы хотел иметь возможность перейти на фабрику, которая создает мой сервисный объект.

Что я нашел до сих пор:

Ответы [ 8 ]

119 голосов
/ 16 марта 2010

Вам потребуется реализовать комбинацию пользовательских ServiceHostFactory, ServiceHost и IInstanceProvider.

Предоставляется сервис с такой подписью конструктора:

* +1007 *

Вот пример, который может ускорить 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
}

Зарегистрируйте MyServiceHostFactory в файле MyService.svc или используйте MyServiceHost непосредственно в коде для сценариев самостоятельного размещения.

Вы можете легко обобщить этот подход, и фактически некоторые DI-контейнеры уже сделали это для вас (пример: Виндзорский центр WCF).

13 голосов
/ 10 февраля 2014

Вы можете просто создать свой экземпляр Service и передать этот экземпляр объекту ServiceHost. Единственное, что вам нужно сделать, это добавить атрибут [ServiceBehaviour] для вашей службы и пометить все возвращаемые объекты атрибутом [DataContract].

Вот макет:

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

и использование:

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

host.Open();

Надеюсь, это облегчит кому-то жизнь.

11 голосов
/ 16 марта 2010

Ответ Марка с IInstanceProvider правильный.

Вместо использования пользовательского ServiceHostFactory вы также можете использовать пользовательский атрибут (скажем, MyInstanceProviderBehaviorAttribute). Получите его из Attribute, сделайте так, чтобы он реализовал IServiceBehavior и реализовали метод IServiceBehavior.ApplyDispatchBehavior, такой как

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

Затем примените атрибут к вашему классу реализации сервиса

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

Третий вариант: вы также можете применить поведение службы, используя файл конфигурации.

5 голосов
/ 11 мая 2013

Я работал с ответом Марка, но (по крайней мере для моего сценария) он был излишне сложным. Один из конструкторов ServiceHost принимает экземпляр службы, который вы можете передать непосредственно из реализации ServiceHostFactory.

Если сравнить с примером Марка, это будет выглядеть так:

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)
    {
    }
}
3 голосов
/ 19 октября 2016

Винт… Я смешал шаблоны внедрения зависимостей и поиска служб (но в основном это все еще внедрение зависимостей, и это даже происходит в конструкторе, что означает, что вы можете иметь состояние только для чтения).

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

Зависимости сервиса четко указаны в контракте его вложенного Dependencies класса. Если вы используете контейнер IoC (тот, который еще не исправил беспорядок в WCF), вы можете настроить его для создания экземпляра Dependencies вместо службы. Таким образом, вы получите теплое нечеткое ощущение, которое дает вам ваш контейнер, и при этом вам не придется прыгать через слишком много обручей, наложенных WCF.

Я не собираюсь терять сон из-за этого подхода. Никто не должен. В конце концов, ваш IoC-контейнер - это большая, толстая, статичная коллекция делегатов, которая создает вещи для вас. Что добавляет еще один?

0 голосов
/ 10 августа 2014

Это было очень полезное решение - особенно для тех, кто является начинающим программистом WCF. Я действительно хотел опубликовать небольшой совет для всех пользователей, которые могут использовать это для службы, размещенной на IIS. MyServiceHost должен наследовать WebServiceHost , а не только ServiceHost.

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

Это создаст все необходимые привязки и т. Д. Для ваших конечных точек в IIS.

0 голосов
/ 08 октября 2013

Я использую статические переменные моего типа. Не уверен, что это лучший способ, но он работает для меня:

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

Когда я создаю экземпляр хоста службы, я делаю следующее:

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

    ...

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

    ....
}
0 голосов
/ 26 июня 2013

Мы столкнулись с этой же проблемой и решили ее следующим образом. Это простое решение.

В Visual Studio просто создайте обычное приложение-службу WCF и удалите его интерфейс. Оставьте файл .cs на месте (просто переименуйте его) и откройте этот файл cs и замените имя интерфейса на исходное имя класса, которое реализует логику службы (таким образом, класс службы использует наследование и заменяет вашу фактическую реализацию). Добавьте конструктор по умолчанию, который вызывает конструкторы базового класса, например:

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

Базовый класс MyService является фактической реализацией службы. Этот базовый класс не должен иметь конструктор без параметров, а только конструкторы с параметрами, которые принимают зависимости.

Служба должна использовать этот класс вместо исходного MyService.

Это простое решение и работает как шарм: -D

...