Расширение размещения с использованием ServiceHostFactory

Стандартный ServiceHost API для размещения служб в Windows Communication Foundation (WCF) — это точка расширяемости в архитектуре WCF. Пользователи могут наследовать свои собственные хост-классы от класса ServiceHost, как правило, чтобы переопределить OnOpening() для использования ServiceDescription с целью принудительного добавления конечных точек по умолчанию или изменения поведений до открытия службы.

В резидентной среде нет необходимости создавать пользовательский класс ServiceHost, поскольку записывается код, создающий узел, а после его создания в нем вызывается метод Open(). В промежутке между этими двумя операциями можно выполнять любые действия. Можно, например, добавить новое поведение IServiceBehavior.

public static void Main()  
{  
   ServiceHost host = new ServiceHost( typeof( MyService ) );  
   host.Description.Add( new MyServiceBehavior() );  
   host.Open();  
  
   ...  
}  

Такой подход нельзя использовать повторно. Код, управляющий описанием, кодируется в программе узла (в данном случае функция Main()), поэтому эту логику трудно повторно использовать в другом контексте. Также существуют другие способы добавления поведения IServiceBehavior, для которых не требуется принудительный код. Можно наследовать атрибут от атрибута ServiceBehaviorAttribute и использовать его в типе реализации службы или можно сделать пользовательское поведение настраиваемым и составить его динамически с использованием конфигурации.

Однако для решения этой проблемы можно также использовать варианты этого примера с незначительными различиями. Один подход заключается в перемещении кода, добавляющего ServiceBehavior, из функции Main() в метод OnOpening пользовательского класса, унаследованного от класса ServiceHost:

public class DerivedHost : ServiceHost  
{  
   public DerivedHost( Type t, params Uri baseAddresses ) :  
      base( t, baseAddresses ) {}  
  
   public override void OnOpening()  
   {  
  this.Description.Add( new MyServiceBehavior() );  
   }  
}  

Затем внутри функции Main() можно использовать следующее.

public static void Main()  
{  
   ServiceHost host = new DerivedHost( typeof( MyService ) );  
   host.Open();  
  
   ...  
}  

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

Способ использования пользовательского класса ServiceHost в службах IIS или службе активации Windows (WAS) неочевиден. Эти среды отличаются от резидентной среды, поскольку среда размещения создает ServiceHost от имени приложения. Инфраструктура размещения IIS и WAS ничего не знает о пользовательском классе, унаследованном от класса ServiceHost.

Фабрика ServiceHostFactory разработана для решения проблемы получения доступа к пользовательскому классу ServiceHost из IIS или WAS. Поскольку пользовательский узел, унаследованный от класса ServiceHost, динамически настроен и потенциально принадлежит к различным типам, среда размещения никогда не создает его напрямую. Вместо этого WCF использует шаблон фабрики для предоставления слоя косвенного взаимодействия между средой размещения и конкретным типом службы. Если не задано иное, используется реализация фабрики ServiceHostFactory по умолчанию, возвращающая экземпляр класса ServiceHost. Но вы также можете предоставить собственную фабрику, которая возвращает производный узел, указав имя типа CLR реализации фабрики в директиве @ServiceHost .

Замысел заключается в том, что в основных случаях реализация собственной фабрики должна иметь простое применение. Например, ниже представлена пользовательская фабрика ServiceHostFactory, возвращающая класс, унаследованный от класса ServiceHost:

public class DerivedFactory : ServiceHostFactory  
{  
   public override ServiceHost CreateServiceHost( Type t, Uri[] baseAddresses )  
   {  
      return new DerivedHost( t, baseAddresses )  
   }  
}  

Чтобы использовать эту фабрику вместо фабрики по умолчанию, укажите имя типа в директиве @ServiceHost следующим образом:

<% @ServiceHost Factory="DerivedFactory" Service="MyService" %>

Хотя нет технических ограничений на операции с классом ServiceHost, возвращаемым методом CreateServiceHost, рекомендуется сохранять реализации фабрики как можно более простыми. Если у вас много пользовательской логики, лучше поместить эту логику внутри узла вместо фабрики, чтобы ее можно было повторно использовать.

Существует еще один уровень размещения API, о котором следует упомянуть здесь. WCF также имеет ServiceHostBase и ServiceHostFactoryBase, от которого ServiceHost и ServiceHostFactory соответственно наследуют. Существуют более сложные сценарии, в которых необходимо выгружать большие части системы метаданных с помощью собственных настроенных созданий.