Доброго времени суток.
Прошу прощения за глупый вопрос — я новичок в WCF, а ответ найти не удается.
Итак, имеется WCF веб-сервис, который принимает запросы от клиентов и передает их на обработку процессорам (предназначенным для обработки запросов логическим компонентам сервера). Экземпляры веб-сервиса одного и того же класса могут "висеть" на разных адресах и передавать полученные запросы одного и того же типа разным процессорам.
Вопрос: как передать веб-сервису, автоматом создаваемому средой WCF при [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] или [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)], ссылку на какой-либо компонент этого же приложения (очередь запросов, процессор и т.п.).
За недостатком знаний наиболее очевидный вариант — использовать статическое поле — член класса веб-сервиса, в него записывать метод — селектор процессора для входящих запросов в зависимости от параметров клиента и веб-сервиса а-ля "void PostRequest(Request request, string clientUri, string serverUri)". Проблема в том, что приложение построено на интерфейсах (тестируемость, DI/IoC), а статические члены классов реализацией интерфейсов не являются.
Добрый день
Обычно для сценария, который вы описали, используются
IInstanceProvider
Примеры его использования совместно с DI/IoC контейнерами есть в сети в большом количестве.
Если вы используете как-то распространенный (например тот же Autfac), то наверняка уже есть готовая реализация.
Ну а если не используете, то вот, например, стартовая статья: как этим пользоваться
WCF Extensibility – IInstanceProvider
Здравствуйте, IvanXXX, Вы писали:
IXX>Вопрос: как передать веб-сервису, автоматом создаваемому средой WCF при [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] или [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)], ссылку на какой-либо компонент этого же приложения (очередь запросов, процессор и т.п.).
Есть толковый пример с описанием для Unity:
http://www.devtrends.co.uk/blog/introducing-unity.wcf-providing-easy-ioc-integration-for-your-wcf-services
https://unitywcf.codeplex.com/
В принципе, допиливается под любой другой IoC контейнер. Главное вдумчиво прочитайте, ибо основное не напортачить со временем жизни объектов и disposables.
Здравствуйте, Михаил.
Относительно
IInstanceProvider: проблема в том, что экземпляр провайдера создается средой WCF путем вызова конструктора без параметров. Если у меня была проблема — передать ссылки на какие-то объекты создаваемому веб-сервису, то теперь проблема — передать их провайдеру, который создает сервис.
Нашел решение попроще:
namespace Server.Impl
{
class Program
{
static void Main(string[] args)
{
// Step 1 Create a URI to serve as the base address.
var baseAddress = new Uri("http://localhost:8000/Server.Impl/");
// Step 2 Create a ServiceHost instance
var host = new MyServiceHost("Initialization data should be here", typeof(Service), baseAddress);
try
{
// Step 3 Add a service endpoint.
host.AddServiceEndpoint(typeof(IService), new WSHttpBinding(), "http://localhost:8000/Server.Impl/service/wshttpbinding");
// Step 4 Enable metadata exchange.
var smb = new ServiceMetadataBehavior {HttpGetEnabled = true};
host.Description.Behaviors.Add(smb);
// Step 5 Start the service.
host.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
// Close the ServiceHostBase to shutdown the service.
host.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
host.Abort();
}
}
}
public class MyServiceHost : ServiceHost
{
public object Data { get; private set; }
public MyServiceHost(object data, Type serviceType, params Uri[] baseAddresses)
:base(serviceType, baseAddresses)
{
Data = data;
}
}
}
Теперь из любого метода веб-сервиса можно обратиться к данным, переданным приложением:
var appData = ((MyServiceHost)OperationContext.Current.Host).Data;
Смущает только очевидная простота схемы по сравнению с расширением WCF через custom IInstanceProvider.
Здравствуйте, IvanXXX, Вы писали:
IXX>Относительно IInstanceProvider: проблема в том, что экземпляр провайдера создается средой WCF путем вызова конструктора без параметров. Если у меня была проблема — передать ссылки на какие-то объекты создаваемому веб-сервису, то теперь проблема — передать их провайдеру, который создает сервис.
Честно, не понял в чем проблема...
Если вы используете Self-Hosting схему, т.е. сами создаете ServiceHost, то что вам мешает сделать, например так:
Создать InstanceProvider с конструктором, в который вы будете передавать все нужные данные (можете сразу совместить его с IServiceBehavior) — реализация есть в статье, на которую я давал ссылку выше.
class InstanceProvider : IInstanceProvider, IServiceBehavior
{
private readonly object _instanceData;
public InstanceProvider(object instanceData)
{
_instanceData = instanceData;
}
// Реализация обоих интерфейсов опущена
}
А далее сделать как-то так:
object instanceData = new object();
var host = new ServiceHost(typeof (Service));
host.Description.Behaviors.Add(new InstanceProvider(instanceData));
Если у вас IIS хостинг, то просто код инициализации хоста переедет в фабрику хостов.
IXX>Теперь из любого метода веб-сервиса можно обратиться к данным, переданным приложением:
IXX>IXX>var appData = ((MyServiceHost)OperationContext.Current.Host).Data;
IXX>
Ну и где вы избавились от неявных зависимостей? Вы же теперь классы привязали даже не к статическому полю, а к полю внутри инфраструктуры. Т.е. для тестирования ваших сервисов, вам теперь придется поднимать всю WCF инфраструктуру (я сильно сомневаюсь, что вы сможете легко создать или замокировать OperationContext и иже с ним.
IXX>Смущает только очевидная простота схемы по сравнению с расширением WCF через custom IInstanceProvider.
Ну так он и сильно хуже.
Помимо того, что вы не избавились от зависимости, а только её усилили (т.к. теперь вы зависите еще и от инфраструктуры WCF, через которою вы получаете все данные), вы также имеете только 1 экземпляр данных, которые можно передать сервису. Т.е. если тут будет какой-нибудь репозиторий, который нужно создавать и уничтожать на каждый запрос — вы этого сделать не сможете, у вас время жизни поля Data равно времени жизни хоста, а не экземпляра сервиса.