这是《WCF服务编程》这本书中的一个辅助类。个人觉得作者的重构的思想不错,故将这个类贴出来,希望能对没有看过这本书的初学者有所帮助。
首先,我先把这个类贴出来:
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description; namespace MyService
{
public class ServiceHost<T> : ServiceHost
{
#region 简化ServiceHost的构造函数
public ServiceHost()
: base(typeof(T))
{ }
public ServiceHost(params string[] baseAddresses)
: base(typeof(T), Convert(baseAddresses))
{ }
public ServiceHost(params Uri[] baseAddresses)
: base(typeof(T), baseAddresses)
{ }
static Uri[] Convert(string[] baseAddresses)
{
Converter<string, Uri> converter = delegate(string address)
{
return new Uri(address);
};
return Array.ConvertAll(baseAddresses, converter);
}
#endregion
#region 简化元数据交换行为的启用和关闭
public bool EnableMetadataExchange
{
set
{
if (State == CommunicationState.Opened)
{
throw new InvalidOperationException("Host is already opened");
}
ServiceMetadataBehavior metadataBehavior;
metadataBehavior
if (metadataBehavior == null)
{
metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetEnabled = true;
Description.Behaviors.Add(metadataBehavior);
}
if (value == true)
{
if (HasMexEndpoint == false)
{
AddAllMexEndPoints();
}
}
}
get
{
ServiceMetadataBehavior metadataBehavior;
metadataBehavior = Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
return false;
}
return metadataBehavior.HttpGetEnabled;
}
}
public bool HasMexEndpoint
{
get
{
Predicate<ServiceEndpoint> mexEndPoint = delegate(ServiceEndpoint endpoint)
{
return endpoint.Contract.ContractType == typeof(IMetadataExchange);
};
return Array.Exists(Description.Endpoints.ToArray<ServiceEndpoint>(), mexEndPoint);
}
}
public void AddAllMexEndPoints()
{
Debug.Assert(HasMexEndpoint == false);
foreach (Uri baseAddress in BaseAddresses)
{
BindingElement bindingElement = null;
switch (baseAddress.Scheme)
{
case "net.tcp":
bindingElement = new TcpTransportBindingElement();
break;
case "http":
bindingElement = new HttpTransportBindingElement();
break;
case "https":
bindingElement = new HttpsTransportBindingElement();
break;
case "net.pipe":
bindingElement = new NamedPipeTransportBindingElement();
break;
}
if (bindingElement != null)
{
Binding binding = new CustomBinding(bindingElement);
AddServiceEndpoint(typeof(IMetadataExchange), binding, "MEX");
}
}
}
#endregion
}
}
这个类继承自ServiceHost,但该类是个泛型类,以服务类型为类型参数,从而简化了创建ServiceHost对象的步骤。
其次这个类还可以通过其EnableMetaldataExchange属性设置元数据交换行为的开启与关闭,在开启元数据交换行为的时候还会为每个基地址添加相应的元数据交换终结点。
这样我们在使用的时候只需将ServiceHost<T>对象的EnableMetadataExchange属性设为true,即可开启该服务的元数据交换行为。
作者对这个类的使用有个示例,下面我把这个示例的源码贴出来。
服务端的服务和契约:
{
[ServiceContract]
public interface IMyContract
{
[OperationContract]
string MyOperation(string text);
}
public class MyService : IMyContract
{
public string MyOperation(string text)
{
return "Hello " + text;
}
}
}
还有一个用于创建宿主对象的工厂:
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Diagnostics; namespace MyService.App_Code
{
public class MyServiceFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
Debug.Assert(serviceType == typeof(MyService));
ServiceHost
<MyService> host = new ServiceHost<MyService>(baseAddresses);host.EnableMetadataExchange = true;
return host;
}
}
}
还有其.svc文件:
其服务端的配置:
<services>
<service name="MyService.App_Code.MyService">
<endpoint address="" binding="basicHttpBinding" contract="MyService.App_Code.IMyContract"/>
</service>
</services>
</system.serviceModel>
这样,我们不通过配置配置文件,即可轻松开启元数据交换行为。
在代码示例中,使用到了ServiceHostFactory这个类。这个类的作用就是在对服务.svc文件的第一次访问时,创建ServiceHost对象。
通过解析定义在.svc文件中的<%ServiceHost%>指令的Factory和Service属性得到ServiceHostFactory和Service的类型,Factory默认为System.ServiceMode.ServiceHostFactory,若被指定自定义的Factory(该Factory必须继承自System.ServiceModel.Activation.ServiceHostFactoryBase)后,将使用自定义的Factory,然后通过反射得到该Factory的对象。最后通过调用Factory对象的CreateServiceHost方法创建ServiceHost对象,以寄宿服务。