Поделиться через


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

Стандартный API ServiceHost для размещения служб в 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 and ServiceHostFactory соответственно. Существуют более сложные сценарии, в которых необходимо выгружать большие части системы метаданных с помощью собственных настроенных созданий.