CustomServiceHost 示例演示如何使用类的ServiceHost自定义派生来更改服务的运行时行为。 此方法提供了一种可重用的替代方法,用于以常用方式配置大量服务。 此示例还演示如何使用 ServiceHostFactory 类在 Internet 信息服务(IIS)或 Windows 进程激活服务(WAS)托管环境中使用自定义 ServiceHost。
关于方案
为防止意外泄露潜在的敏感服务元数据,Windows Communication Foundation (WCF) 服务的默认配置会禁用元数据发布。 默认情况下,此行为是安全的,但也意味着不能使用元数据导入工具(如 Svcutil.exe)生成调用服务所需的客户端代码,除非在配置中显式启用服务的元数据发布行为。
为大量服务启用元数据发布涉及向每个单个服务添加相同的配置元素,这会导致大量基本相同的配置信息。 作为单独配置每个服务的替代方法,可以编写命令性代码,该代码允许元数据发布一次,然后在多个不同的服务中重复使用该代码。 这是通过创建派生自 ServiceHost 和替代 ApplyConfiguration
() 方法的新类来强制添加元数据发布行为来实现的。
重要
为了清楚起见,此示例演示如何创建不安全的元数据发布终结点。 此类终结点可能可供匿名未经身份验证的使用者使用,在部署此类终结点之前必须小心,以确保公开披露服务的元数据是适当的。
实现自定义的服务主机
该 ServiceHost 类公开了几个有用的虚拟方法,继承者可以重写这些方法来更改服务的运行时行为。 例如, ApplyConfiguration
() 方法从配置存储中读取服务配置信息,并相应地更改主机 ServiceDescription 。 默认实现从应用程序的配置文件读取配置。 自定义实现可以重写 ApplyConfiguration
() 以便使用命令性代码进一步改变 ServiceDescription,甚至完全替换默认配置存储。 例如,从数据库而不是应用程序的配置文件读取服务的终结点配置。
在本示例中,我们要生成一个用于添加 ServiceMetadataBehavior(可启用元数据发布)的自定义 ServiceHost,即使服务的配置文件中未显式添加此行为。 为了实现此目的,请创建一个从 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
() 必须执行的最后一步是添加默认元数据终结点。 按照约定,将为服务主机的 BaseAddresses 集合中的每个 URI 创建一个元数据终结点。
//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 类来承载服务一样。 但是,由于添加了用于在自定义主机内启用元数据发布的逻辑,因此我们不再需要在配置中显式启用元数据发布行为。 在生成包含多个服务的应用程序时,此方法具有明显的优势,并且想要在它们上启用元数据发布,而无需一遍又一遍地编写相同的配置元素。
在 IIS 或 WAS 中使用自定义 ServiceHost
在自承载方案中使用自定义服务主机非常简单,因为应用程序代码最终负责创建和打开服务主机实例。 但是,在 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()
实例化服务主机本身。
运行示例
尽管此示例确实提供功能齐全的客户端和服务实现,但此示例的要点是说明如何通过自定义主机更改服务的运行时行为。请执行以下步骤:
观察自定义主机的效果
打开服务的 Web.config 文件并观察其中的配置是否未显式启用服务的元数据。
打开服务的 .svc 文件并观察其 @ServiceHost 指令包含一个工厂属性,该属性指定自定义 ServiceHostFactory 的名称。
设置、生成和运行示例
确保已为 Windows Communication Foundation 示例 执行One-Time 安装过程。
要生成解决方案,请按照生成 Windows Communication Foundation 示例中的说明进行操作。
生成解决方案后,请运行 Setup.bat 以在 IIS 7.0 中设置 ServiceModelSamples 应用程序。 ServiceModelSamples 目录现在应显示为 IIS 7.0 应用程序。
若要在单台计算机或跨计算机配置中运行示例,请按照 运行 Windows Communication Foundation 示例中的说明进行操作。
若要删除 IIS 7.0 应用程序,请运行 Cleanup.bat。