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


Пользовательский узел службы

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

Aa395224.Important(ru-ru,VS.100).gif Примечание
Образцы уже могут быть установлены на компьютере. Перед продолжением проверьте следующий каталог (по умолчанию).

<диск_установки>:\WF_WCF_Samples

Если этот каталог не существует, перейдите на страницу Образцы Windows Communication Foundation (WCF) и Windows Workflow Foundation (WF) для .NET Framework 4, чтобы загрузить все образцы Windows Communication Foundation (WCF) и WF. Этот образец расположен в следующем каталоге.

<диск_установки>:\WF_WCF_Samples\WCF\Extensibility\Hosting\CustomServiceHost

Сценарий

Для предотвращения непреднамеренного разглашения потенциально важных метаданных службы, в конфигурации службы Windows Communication Foundation (WCF) публикация метаданных по умолчанию отключена. Такое расширение функциональности по умолчанию защищено, но это также означает, что при этом невозможно использовать средство импорта метаданных (например, Svcutil.exe) для создания клиентского кода, необходимого для вызова службы, если поведение публикации не включено явно в конфигурации.

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

Aa395224.Important(ru-ru,VS.100).gif Примечание
Для ясности этот образец демонстрирует создание незащищенной конечной точки публикации метаданных. Такие конечные точки являются потенциально доступными для анонимных не прошедших проверку подлинности потребителей, поэтому перед развертыванием таких конечных точек следует соблюдать осторожность и убедиться, что публичное раскрытие метаданных службы уместно.

Реализация пользовательского класса ServiceHost

Класс ServiceHost предоставляет несколько полезных виртуальных методов, которые наследующие классы могут переопределять, чтобы изменять поведение службы во время выполнения. Например, метод ApplyConfiguration() выполняет чтение сведений конфигурации службы из хранилища конфигураций и соответствующим образом изменяет объект ServiceDescription основного приложения. Реализация по умолчанию считывает конфигурацию из файла конфигурации приложения. Пользовательские реализации могут переопределять метод ApplyConfiguration() для дополнительного измененияServiceDescription с помощью императивного кода или даже полностью заменять хранилище конфигураций по умолчанию. Например, чтобы читать конфигурацию конечной точки службы не из файла конфигурации приложения, а из базы данных.

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

class SelfDescribingServiceHost : ServiceHost
{
    public SelfDescribingServiceHost(Type serviceType, params Uri[] baseAddresses)
        : base(serviceType, baseAddresses) { }

    //Overriding ApplyConfiguration() allows us to 
    //alter the ServiceDescription prior to opening
    //the service host. 
    protected override void ApplyConfiguration()
    {
        //First, we call base.ApplyConfiguration()
        //to read any configuration that was provided for
        //the service we're hosting. After this call,
        //this.Description describes the service
        //as it was configured.
        base.ApplyConfiguration();     

        //(rest of implementation elided for clarity)
    }
}

Так как игнорировать конфигурацию, предоставленную в файле конфигурации приложения, не нужно, переопределение ApplyConfiguration() в первую очередь вызывает базовую реализацию. После выполнения этого метода можно принудительно добавить в описание поведение ServiceMetadataBehavior с помощью следующего принудительного кода.

ServiceMetadataBehavior mexBehavior = this.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (mexBehavior == null)
{
    mexBehavior = new ServiceMetadataBehavior();
    this.Description.Behaviors.Add(mexBehavior);
}
else
{
    //Metadata behavior has already been configured, 
    //so we don't have any work to do.
    return;
}

Наконец, переопределение ApplyConfiguration() должно добавить конечную точку метаданных по умолчанию. По соглашению для каждого универсального кода ресурса (URI) в коллекции BaseAddresses главного приложения службы создается по одной конечной точке метаданных.

//Add a metadata endpoint at each base address
//using the "/mex" addressing convention
foreach (Uri baseAddress in this.BaseAddresses)
{
    if (baseAddress.Scheme == Uri.UriSchemeHttp)
    {
        mexBehavior.HttpGetEnabled = true;
        this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
                                MetadataExchangeBindings.CreateMexHttpBinding(),
                                "mex");
    }
    else if (baseAddress.Scheme == Uri.UriSchemeHttps)
    {
        mexBehavior.HttpsGetEnabled = true;
        this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
                                MetadataExchangeBindings.CreateMexHttpsBinding(),
                                "mex");
    }
    else if (baseAddress.Scheme == Uri.UriSchemeNetPipe)
    {
        this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
                                MetadataExchangeBindings.CreateMexNamedPipeBinding(),
                                "mex");
    }
    else if (baseAddress.Scheme == Uri.UriSchemeNetTcp)
    {
        this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
                                MetadataExchangeBindings.CreateMexTcpBinding(),
                                "mex");
    }
}

Использование пользовательского ServiceHost в резидентной службе

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

    SelfDescribingServiceHost host = 
             new SelfDescribingServiceHost( typeof( Calculator ) );
    host.Open();

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

Использование пользовательского класса ServiceHost в службах IIS или WAS

Использовать пользовательский узел службы в резидентных сценариях нетрудно, потому что за создание и открытие экземпляра основного приложения службы отвечает в итоге код вашего приложения. Однако в среде размещения IIS или WAS инфраструктура WCF динамически создает экземпляры основного приложения службы в ответ на входящие сообщения. В этой среде размещения также можно использовать пользовательские узлы служб, но для этого требуется дополнительный код в виде ServiceHostFactory. В следующем коде приведен класс, наследуемый от ServiceHostFactory, возвращающий экземпляры пользовательского SelfDescribingServiceHost.

public class SelfDescribingServiceHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, 
     Uri[] baseAddresses)
    {
        //All the custom factory does is return a new instance
        //of our custom host class. The bulk of the custom logic should
        //live in the custom host (as opposed to the factory) 
        //for maximum
        //reuse value outside of the IIS/WAS hosting environment.
        return new SelfDescribingServiceHost(serviceType,   
                                             baseAddresses);
    }
}

Реализация пользовательской фабрики ServiceHostFactory тоже вполне очевидна. Вся пользовательская логика находится внутри реализации ServiceHost; фабрика возвращает экземпляр производного класса.

Для использования пользовательской фабрики с реализацией службы необходимо добавить в файл SVC службы дополнительные метаданные.

<%@ServiceHost Service="Microsoft.ServiceModel.Samples.CalculatorService"
               Factory="Microsoft.ServiceModel.Samples.SelfDescribingServiceHostFactory"
               language=c# Debug="true" %>

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

Выполнение образца

В этом образце содержатся полностью функциональные реализации клиента и службы, но цель этого образца — продемонстрировать изменение поведения службы во время выполнения с помощью пользовательского ведущего приложения, выполните следующие действия.

Действие пользовательского ведущего приложения

  1. Откройте файл Web.config службы и убедитесь, что там нет конфигурации, явно включающей для службы метаданные.

  2. Откройте файл SVC службы и убедитесь, что директива @ServiceHost содержит атрибут Factory, задающий имя пользовательской фабрики ServiceHostFactory.

Настройка, построение и выполнение образца

  1. Убедитесь, что выполнена процедура, описанная в разделе Процедура однократной настройки образцов Windows Communication Foundation.

  2. Чтобы построить решение, следуйте инструкциям в разделе Построение образцов Windows Communication Foundation.

  3. После построения решения запустите файл Setup.bat, чтобы настроить приложение ServiceModelSamples в службах IIS 7.0. Теперь каталог ServiceModelSamples должен представляться как приложение IIS 7.0.

  4. Чтобы выполнить образец на одном или нескольких компьютерах, следуйте инструкциям в разделе Running the Windows Communication Foundation Samples.

  5. Чтобы удалить приложение IIS 7.0, выполните файл Cleanup.bat.

См. также

Задачи

Как разместить службу WCF в IIS