Внедрение данных в службу WCF
У меня есть WCF-сервисы, структурированные как предложено Мигелем Кастро. Это означает, что я настроил все вручную, и у меня есть консольное приложение, в котором размещаются мои службы с использованием объектов ServiceHost.
Я хочу сохранить свои классы обслуживания тонкими, и в настоящее время они просто передают вызовы к классам поведения. Моя проблема сейчас заключается в модульном тестировании классов обслуживания. Я хочу внедрить что-то в классы в качестве параметра конструктора, чтобы я мог смоделировать это и написать надлежащие изолированные модульные тесты. Класс ServiceHost, похоже, не принимает аргументы, поэтому мой вопрос в том, как я могу вводить данные в классы обслуживания - или нет?
4 ответа
WCF поддерживает конструктор Injection, но вам нужно прыгнуть через несколько обручей, чтобы добраться туда. Ключ лежит в написании пользовательского ServiceHostFactory. Хотя в нем также должен быть конструктор по умолчанию, вы можете использовать его, чтобы связать все правильные поведения.
В качестве примера я недавно написал тот, который использует Castle Windsor для подключения зависимостей для реализации сервиса. Реализация CreateServiceHost просто делает это:
return new WindsorServiceHost(this.container, serviceType, baseAddresses);
где this.container
настроенный IWindsorContainer
WindsorServiceHost выглядит так:
public class WindsorServiceHost : ServiceHost
{
public WindsorServiceHost(IWindsorContainer container, Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
foreach (var cd in this.ImplementedContracts.Values)
{
cd.Behaviors.Add(new WindsorInstanceProvider(container));
}
}
}
и WindsorInstanceProvider выглядит так:
public class WindsorInstanceProvider : IInstanceProvider, IContractBehavior
{
private readonly IWindsorContainer container;
public WindsorInstanceProvider(IWindsorContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this.container = container;
}
#region IInstanceProvider Members
public object GetInstance(InstanceContext instanceContext, Message message)
{
return this.GetInstance(instanceContext);
}
public object GetInstance(InstanceContext instanceContext)
{
var serviceType = instanceContext.Host.Description.ServiceType;
return this.container.Resolve(serviceType);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
this.container.Release(instance);
}
#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
}
Это может выглядеть много, но обратите внимание, что это универсальный код многократного использования, который имеет довольно низкую цикломатическую сложность.
Вы можете следовать той же идиоме кодирования, чтобы внедрить Dependency Injection с другим DI-контейнером или с помощью DI от Poor Man.
Вот более старая рецензия на эту идиому, в которой используется DI Бедного Человека.
Если вы использовали Castle Windsor, у него есть отличная интеграция с WCF, которая позволяет вам это делать, и намного проще.
Вы настроили свой сервис как синглтон? я обнаружил, что реализации IInstanceProvider могут быть проблематичными при использовании контейнера DI для создания экземпляров службы.
Статья " Размещение макета как службы WCF" содержит статический метод, который будет генерировать хост службы WCF на основе объекта, который передается в метод с одной конечной точкой.
Этот метод был также опубликован в ответе " Рекомендуемые шаблоны для модульного тестирования веб-служб".
Пример использования вызывает NSubstitute, но могут использоваться и другие насмешливые freameworks.