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

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

О сценарии

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

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

Внимание

Для ясности этот образец демонстрирует создание незащищенной конечной точки публикации метаданных. Такие конечные точки потенциально доступны анонимным неавторентированным потребителям и должны быть приняты меры, прежде чем развертывать такие конечные точки, чтобы обеспечить общедоступное раскрытие метаданных службы.

Реализация пользовательского 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 do not 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" %>

Здесь мы добавили дополнительный Factory атрибут в @ServiceHost директиву и передали имя типа CLR пользовательской фабрики в качестве значения атрибута. Когда СЛУЖБА IIS или WAS получает сообщение для этой службы, инфраструктура размещения WCF сначала создает экземпляр ServiceHostFactory, а затем создает экземпляр узла службы путем вызова ServiceHostFactory.CreateServiceHost().

Выполнение примера

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

Наблюдение за эффектом пользовательского узла

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

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

Настройка, сборка и запуск примера

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

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

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

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

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

См. также