在WCF中完成同样的功能一般有2种选择:手工编码和撰写配置文件。MEX(Metadata Exchange)端点用于服务向外界发布其元数据,在WCF编程中非常常见。本文通过继承ServiceHost,编写一个简单的类来简化MEX的编程。通过这个扩展的类,不仅可以减少手工编码量,也可以减少配置文件的长度。
首先定义一个CollectionHelper类,该类提供了一个静态的泛型方法,用于判断一个实现ICollection接口的容器中是否有满足断言Predicate<T>的数据,代码实现如下:
namespace Kwan.WCFHelperLibrary
{
public static class CollectionHelper
{
public static Boolean Exists < T > (ICollection < T > items, Predicate < T > predicate)
{
foreach (T item in items)
{
if (predicate(item) == true )
return true ;
}
return false ;
}
}
}
然后开始编写扩展的ServiceHost<T>类,该类继承于ServiceHost,类型参数作为调用ServiceHost构造器的参数。ServiceHost<T>的基本定义和构造器如下:
/// 该类用于简化MEX EndPoint编程
/// </summary>
/// <typeparam name="T"></typeparam>
public class ServiceHost < T > : ServiceHost
{
/// <summary>
/// 类构造器,调用基类构造器
/// </summary>
/// <param name="baseAddresses"> 基地址 </param>
public ServiceHost( params Uri[] baseAddresses)
: base ( typeof (T), baseAddresses)
{
// 设置EnableMetadataExchange默认值为true
EnableMetadataExchange = true ;
}
ServiceHost<T>定义了2个Property。第一个Property为布尔型的HasMexEndPoints,用于指示该ServiceHost<T>实例是否含有MEX类型的端点
{
get
{
// 使用匿名方法为Predicate<ServiceEndpoint>指定过程
Predicate < ServiceEndpoint > isMexEndPoint =
delegate (ServiceEndpoint endpoint)
{
// 如果endpoint的契约类型为IMetadataExchange,则返回真值
return endpoint.Contract.ContractType == typeof (IMetadataExchange);
};
// 调用CollectionHelper的静态方法判断当前ServiceHost中是否存在MEX EndPoints
return CollectionHelper.Exists(Description.Endpoints, isMexEndPoint);
}
}
在判断是否包含MEX类型的端点的代码中使用到了预先定义的CollectionHelper类中的泛型方法,使用匿名方法,保证代码的简洁。
第二个Property是该类的关键部分,布尔型的EnableMetadataExchange用于指示/设置该ServiceHost<T>实例是否允许发布元数据。实现如下:
/// 是否允许MEX
/// </summary>
public Boolean EnableMetadataExchange
{
get
{
ServiceMetadataBehavior metadataBehavior;
metadataBehavior = Description.Behaviors.Find < ServiceMetadataBehavior > ();
if (metadataBehavior == null )
{
return false ;
}
return metadataBehavior.HttpGetEnabled;
}
set
{
// 在ServiceHost打开的情况下不能设置该Property
if (State == CommunicationState.Opened)
{
throw new InvalidOperationException( " Host is already opened " );
}
ServiceMetadataBehavior metadataBehavior
= Description.Behaviors.Find < ServiceMetadataBehavior > ();
if (metadataBehavior == null )
{
metadataBehavior = new ServiceMetadataBehavior();
Description.Behaviors.Add(metadataBehavior);
}
metadataBehavior.HttpGetEnabled = value;
// 如果设置该Property为false,则移除所有MEX EndPoints
// 如果设置该Property为true,则添加所有MEX EndPoints
if (value == false )
{
RemoveAllMexEndPoints();
}
else
{
if (HasMexEndPoints == false )
AddAllMexEndPoints();
}
}
}
在该Property的Setter中调用到了2个方法用于添加或者移除所有的MEX端点,而端点的信息来自于BaseAddresses成员,实现如下:
/// 添加所有的MEX EndPoints
/// </summary>
private void AddAllMexEndPoints()
{
foreach (Uri baseAddress in BaseAddresses)
{
BindingElement bindingElement = null ;
switch (baseAddress.Scheme)
{
case " net.tcp " :
{
bindingElement = new TcpTransportBindingElement();
break ;
}
case " net.pipe " :
{
bindingElement = new NamedPipeTransportBindingElement();
break ;
}
case " http " :
{
bindingElement = new HttpsTransportBindingElement();
break ;
}
case " https " :
{
bindingElement = new HttpsTransportBindingElement();
break ;
}
}
if (bindingElement != null )
{
Binding binding = new CustomBinding(bindingElement);
AddServiceEndpoint( typeof (IMetadataExchange), binding, " MEX " );
}
}
}
/// <summary>
/// 移除所有的MEX EndPoints
/// </summary>
private void RemoveAllMexEndPoints()
{
for ( int i = 0 ; i < Description.Endpoints.Count; i ++ )
{
if (Description.Endpoints[i].Contract.ContractType == typeof (IMetadataExchange))
{
Description.Endpoints.RemoveAt(i);
i -- ;
}
}
}
现在我们来使用这个新的ServiceHost<T>,实例化。默认的情况下,EnableMetadataExchange为真,若在打开这个ServiceHost<T>之前将其设置为假,则将移除所有的MEX类型的端点。
在未来,该类将被加以扩展,以进一步简化WCF的宿主端的编程。