Как передать значения конструктору в моей службе wcf?

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

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

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

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

Ответ 1

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

Для службы с этой конструкторской сигнатурой:

public MyService(IDependency dep)

Вот пример, который может развернуть 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-контейнеры уже сделали это для вас (cue: Windsor WCF Facility).

Ответ 2

Вы можете просто создать и экземпляр вашего 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();

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

Ответ 3

Отметьте ответ с помощью 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

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

Ответ 4

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

Чтобы воспроизвести пример Mark, он будет выглядеть так:

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

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

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 - это большая, толстая, статическая коллекция делегатов, которая создает для вас материал. Что добавляет еще один?

Ответ 6

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

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

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

Ответ 7

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

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

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

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

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

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

Ответ 8

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

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

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

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

    ...

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

    ....
}