使用通道
我们可以直接使用通道调用服务的操作,而无须借助于代理类。ChannelFactory<T>类(以及它所支持的类型)有助于我们轻松地创建代理,如例1-21所示。
例1-21:ChannelFactory<T>类
public class ContractDescription
{
public Type ContractType
{get;set;}
//更多成员
}
public class ServiceEndpoint
{
public ServiceEndpoint(ContractDescription contract,Binding binding,
EndpointAddress address);
public EndpointAddress Address
{get;set;}
public Binding Binding
{get;set;}
public ContractDescription Contract
{get;}
//更多成员
}
public abstract class ChannelFactory : ...
{
public ServiceEndpoint Endpoint
{get;}
//更多成员
}
public class ChannelFactory<T> : ChannelFactory,...
{
public ChannelFactory(ServiceEndpoint endpoint);
public ChannelFactory(string configurationName);
public ChannelFactory(Binding binding,EndpointAddress endpointAddress);
public static T CreateChannel(Binding binding,EndpointAddress endpointAddress);
public T CreateChannel();
//更多成员
}
我们需要向ChannelFactory<T>类的构造函数传递一个终结点对象,终结点名称可以从客户端配置文件中获取;或者传递绑定与地址对象,或者传递ServiceEndpoint对象。接着,调用CreateChannel()方法获得代理的引用,然后使用代理的方法。最后,通过将代理强制转换为IDisposable类型,调用Dispose()方法关闭代理。当然,也可以将代理强制转换为ICommunicationObject类型,通过调用Close()方法关闭代理:
ChannelFactory<IMyContract> factory = new ChannelFactory<IMyContract>();
IMyContract proxy1 = factory.CreateChannel();
using(proxy1 as IDisposable)
{
proxy1.MyMethod();
}
IMyContract proxy2 = factory.CreateChannel();
proxy2.MyMethod();
ICommunicationObject channel = proxy2 as ICommunicationObject;
Debug.Assert(channel != null);
channel.Close();
我们还可以直接调用CreateChannel()静态方法,根据给定的绑定和地址值创建代理,这样就不需要创建ChannelFactory<T>类的实例了。
Binding binding = new NetTcpBinding();
EndpointAddress address = new EndpointAddress("net.tcp://localhost:8000");
IMyContract proxy = ChannelFactory<IMyContract>.CreateChannel(binding,address);
using(proxy as IDisposable)
{
proxy1.MyMethod();
}
InProcFactory类
为了演示ChannelFactory<T>类的强大功能,考虑静态辅助类InProcFactory,定义如下:
public static class InProcFactory
{
public static I CreateInstance<S,I>() where I : class
where S : I;
public static void CloseProxy<I>(I instance) where I : class;
//更多成员
}
设计InProcFactory类的目的在于简化并自动实现进程内托管。CreateInstance()方法定义了两个泛型类型参数:服务类型S以及服务支持的契约类型I。CreateInstance()要求S必须派生自I。可以直接使用InProcFactory类:
IMyContract proxy = InProcFactory.CreateInstance<MyService,IMyContract>();
proxy.MyMethod();
InProcFactory.CloseProxy(proxy);
InProcFactory能够接收一个服务类,并将它升级为WCF服务。这就好比我们可以在WCF中调用旧的Win32函数LoadLibrary()。
InProcFactory<T>的实现
所有的进程内调用都应该使用命名管道,同时还应该传递所有事务。我们可以使用编程配置自动地配置客户端和服务,同时使用ChannelFactory<T>,以避免对代理对象的使用。例1-22演示了InProcFactory的实现。为简便起见,省略了一部分与此无关的代码。
例1-22:InProcFactory类
public static class InProcFactory
{
struct HostRecord
{
public HostRecord(ServiceHost host,string address)
{
Host = host;
Address = address;
}
public readonly ServiceHost Host;
public readonly string Address;
}
static readonly Uri BaseAddress = new Uri("net.pipe://localhost/" + Guid.NewGuid().ToString());
static readonly Binding NamedPipeBinding;
static Dictionary<Type,HostRecord> m_Hosts = new Dictionary<Type,HostRecord>();
static InProcFactory()
{
NetNamedPipeBinding binding = new NetNamedPipeBinding();
binding.TransactionFlow = true;
NamedPipeBinding = binding;
AppDomain.CurrentDomain.ProcessExit += delegate
{
foreach(KeyValuePair<Type,HostRecord> pair in m_Hosts)
{
pair.Value.Host.Close();
}
};
}
public static I CreateInstance<S,I>() where I : class
where S : I
{
HostRecord hostRecord = GetHostRecord<S,I>();
return ChannelFactory<I>.CreateChannel(NamedPipeBinding,
new EndpointAddress(hostRecord.Address));
}
static HostRecord GetHostRecord<S,I>() where I : class
where S : I
{
HostRecord hostRecord;
if(m_Hosts.ContainsKey(typeof(S)))
{
hostRecord = m_Hosts[typeof(S)];
}
Else
{
ServiceHost host = new ServiceHost(typeof(S), BaseAddress);
string address = BaseAddress.ToString() + Guid.NewGuid().ToString();
hostRecord = new HostRecord(host,address);
m_Hosts.Add(typeof(S),hostRecord);
host.AddServiceEndpoint(typeof(I),NamedPipeBinding,address);
host.Open();
}
return hostRecord;
}
public static void CloseProxy<I>(I instance) where I : class
{
ICommunicationObject proxy = instance as ICommunicationObject;
Debug.Assert(proxy != null);
proxy.Close();
}
}
实现InProcFactory所面临的最大挑战,就是如何实现CreateInstance()方法,使得它能够为每个类型创建服务实例。由于每个服务类型都必须有一个对应的宿主(ServiceHost的一个实例),如果为每次调用都分配一个宿主实例,算不上是一个好的办法。问题在于,如果需要为相同的类型实例化第二个对象,CreateInstance()方法应该怎么做:
IMyContract proxy1 = InProcFactory.CreateInstance<MyService,IMyContract>();
IMyContract proxy2 = InProcFactory.CreateInstance<MyService,IMyContract>();
解决办法是在内部管理一个字典(Dictionary)集合,以建立服务类型与特定的宿主实例之间的映射。调用CreateInstance()方法创建指定类型的实例时,通过调用辅助方法GetHostRecord()查找字典集合,只有不存在该服务类型,才会创建宿主实例。如果需要创建宿主,GetHostRecord()方法会以编程方式为宿主添加一个终结点,并生成GUID作为唯一标识的管道名。GetHostRecord()方法会返回一个HostRecord对象。HostRecord是一个结构类型,定义了ServiceHost实例与地址值。然后,CreateInstance()根据HostRecord对象获得终结点地址,调用ChannelFactory<T>类的方法创建代理。在InProcFactory类的静态构造函数(静态构造函数会在初始化类时调用)中,InProcFactory订阅了ProcessExit事件,使用了匿名方法在停止进程时关闭所有宿主。最后,为了方便客户端关闭代理,InProcFactory定义了CloseProxy()方法,它将代理强制转换为ICommunicationObject类型以关闭代理。





