创buildWCF ChannelFactory <T>

我试图将现有的.NET Remoting应用程序转换为WCF。 服务器和客户端共享通用接口,所有对象都是服务器激活的对象。

在WCF世界中,这将与创build每个通话服务和使用ChannelFactory<T>创build代理类似。 我正在为如何为ASP.NET客户端正确创buildChannelFactory<T>而烦恼。

出于性能原因,我想cachingChannelFactory<T>对象,并且每次调用服务时都创build通道。 在.NET的远程处理date,曾经有RemotingConfiguration.GetRegisteredWellknownClientTypes()方法来获取我可以caching的客户端对象的集合。 看起来,在WCF世界里没有这样的事情,虽然我能从configuration文件中获取端点集合。

现在这是我认为会起作用的。 我可以创build这样的东西:

 public static ProxyHelper { static Dictionary<Type, object> lookup = new Dictionary<string, object>(); static public T GetChannel<T>() { Type type = typeof(T); ChannelFactory<T> factory; if (!lookup.ContainsKey(type)) { factory = new ChannelFactory<T>(); lookup.Add(type, factory); } else { factory = (ChannelFactory<T>)lookup[type]; } T proxy = factory.CreateChannel(); ((IClientChannel)proxy).Open(); return proxy; } } 

我认为上面的代码会工作,但是我有点担心multithreading试图添加新的ChannelFactory<T>对象,如果它不在查找中。 因为我使用.NET 4.0,所以我在考虑使用ConcurrentDictionary并使用GetOrAdd()方法或者首先使用TryGetValue()方法来检查ChannelFactory<T>存在并且不存在,然后使用GetOrAdd()方法。 不知道ConcurrentDictionary.TryGetValue()ConcurrentDictionary.GetOrAdd()方法的性能。

另一个小问题是我是否需要在ASP.NET应用程序结束后调用通道工厂对象的ChannelFactory.Close()方法,或者我可以让.NET框架自行处理通道工厂对象。 通过使用((IChannel)proxy).Close()方法调用服务方法后,代理通道将始终closures。

是的,如果你想创build类似这样的东西 – 一个静态类来保存所有的ChannelFactory<T>实例 – 你一定要确保这个类是100%线程安全的,并且在同时访问时不会绊倒。 我还没有使用.NET 4的function,所以我不能评论那些具体的 – 但我肯定会build议尽可能安全。

至于你的第二个(小问题):ChannelFactory本身是一个静态类 – 所以你不能真的调用一个.Close()方法。 如果您打算询问是否要在实际的IChannel上调用.Close()方法,那么再次重申:是的,如果可以的话,尽力成为一名优秀的公民并closures这些渠道。 如果你错过了一个,.NET会照顾它 – 但是不要把你的未使用的频道扔在地板上继续 – 自己清理! 🙂

以下是我用来处理渠道工厂的帮助程序类:

 public class ChannelFactoryManager : IDisposable { private static Dictionary<Type, ChannelFactory> _factories = new Dictionary<Type,ChannelFactory>(); private static readonly object _syncRoot = new object(); public virtual T CreateChannel<T>() where T : class { return CreateChannel<T>("*", null); } public virtual T CreateChannel<T>(string endpointConfigurationName) where T : class { return CreateChannel<T>(endpointConfigurationName, null); } public virtual T CreateChannel<T>(string endpointConfigurationName, string endpointAddress) where T : class { T local = GetFactory<T>(endpointConfigurationName, endpointAddress).CreateChannel(); ((IClientChannel)local).Faulted += ChannelFaulted; return local; } protected virtual ChannelFactory<T> GetFactory<T>(string endpointConfigurationName, string endpointAddress) where T : class { lock (_syncRoot) { ChannelFactory factory; if (!_factories.TryGetValue(typeof(T), out factory)) { factory = CreateFactoryInstance<T>(endpointConfigurationName, endpointAddress); _factories.Add(typeof(T), factory); } return (factory as ChannelFactory<T>); } } private ChannelFactory CreateFactoryInstance<T>(string endpointConfigurationName, string endpointAddress) { ChannelFactory factory = null; if (!string.IsNullOrEmpty(endpointAddress)) { factory = new ChannelFactory<T>(endpointConfigurationName, new EndpointAddress(endpointAddress)); } else { factory = new ChannelFactory<T>(endpointConfigurationName); } factory.Faulted += FactoryFaulted; factory.Open(); return factory; } private void ChannelFaulted(object sender, EventArgs e) { IClientChannel channel = (IClientChannel)sender; try { channel.Close(); } catch { channel.Abort(); } throw new ApplicationException("Exc_ChannelFailure"); } private void FactoryFaulted(object sender, EventArgs args) { ChannelFactory factory = (ChannelFactory)sender; try { factory.Close(); } catch { factory.Abort(); } Type[] genericArguments = factory.GetType().GetGenericArguments(); if ((genericArguments != null) && (genericArguments.Length == 1)) { Type key = genericArguments[0]; if (_factories.ContainsKey(key)) { _factories.Remove(key); } } throw new ApplicationException("Exc_ChannelFactoryFailure"); } public void Dispose() { Dispose(true); } protected virtual void Dispose(bool disposing) { if (disposing) { lock (_syncRoot) { foreach (Type type in _factories.Keys) { ChannelFactory factory = _factories[type]; try { factory.Close(); continue; } catch { factory.Abort(); continue; } } _factories.Clear(); } } } } 

然后我定义一个服务调用者:

 public interface IServiceInvoker { R InvokeService<T, R>(Func<T, R> invokeHandler) where T: class; } 

和一个实现:

 public class WCFServiceInvoker : IServiceInvoker { private static ChannelFactoryManager _factoryManager = new ChannelFactoryManager(); private static ClientSection _clientSection = ConfigurationManager.GetSection("system.serviceModel/client") as ClientSection; public R InvokeService<T, R>(Func<T, R> invokeHandler) where T : class { var endpointNameAddressPair = GetEndpointNameAddressPair(typeof(T)); T arg = _factoryManager.CreateChannel<T>(endpointNameAddressPair.Key, endpointNameAddressPair.Value); ICommunicationObject obj2 = (ICommunicationObject)arg; try { return invokeHandler(arg); } finally { try { if (obj2.State != CommunicationState.Faulted) { obj2.Close(); } } catch { obj2.Abort(); } } } private KeyValuePair<string, string> GetEndpointNameAddressPair(Type serviceContractType) { var configException = new ConfigurationErrorsException(string.Format("No client endpoint found for type {0}. Please add the section <client><endpoint name=\"myservice\" address=\"http://address/\" binding=\"basicHttpBinding\" contract=\"{0}\"/></client> in the config file.", serviceContractType)); if (((_clientSection == null) || (_clientSection.Endpoints == null)) || (_clientSection.Endpoints.Count < 1)) { throw configException; } foreach (ChannelEndpointElement element in _clientSection.Endpoints) { if (element.Contract == serviceContractType.ToString()) { return new KeyValuePair<string, string>(element.Name, element.Address.AbsoluteUri); } } throw configException; } } 

现在每次你需要调用一个WCF服务,你可以使用这个:

 WCFServiceInvoker invoker = new WCFServiceInvoker(); SomeReturnType result = invoker.InvokeService<IMyServiceContract, SomeReturnType>( proxy => proxy.SomeMethod() ); 

这假定您已经在configuration文件中为IMyServiceContract服务协定定义了客户端端点:

 <client> <endpoint name="myservice" address="http://example.com/" binding="basicHttpBinding" contract="IMyServiceContract" /> </client> 

我不喜欢这个呼叫build筑:

 WCFServiceInvoker invoker = new WCFServiceInvoker(); var result = invoker.InvokeService<IClaimsService, ICollection<string>>(proxy => proxy.GetStringClaims()); 

你也不能使用同一个频道两次。

我创build了这个解决scheme:

 using(var i = Connection<IClaimsService>.Instance) { var result = i.Channel.GetStringClaims(); } 

现在,您可以重复使用相同的通道,直到using语句调用dispose为止。

GetChannel方法基本上是一个ChannelFactory.CreateChannel(),我正在使用一些额外的configuration。

像其他解决scheme一样,您可以为ChannelFactory创build一些caching。

Connnection类的代码:

 public static class Connection<T> { public static ChannelHolder Instance { get { return new ChannelHolder(); } } public class ChannelHolder : IDisposable { public T Channel { get; set; } public ChannelHolder() { this.Channel = GetChannel(); } public void Dispose() { IChannel connection = null; try { connection = (IChannel)Channel; connection.Close(); } catch (Exception) { if (connection != null) { connection.Abort(); } } } } } 

@NelsonRothermel,是的,我没有在ChannelFactoryManager ChannelFaulted事件处理程序中使用try catch。 所以ChannelFaulted将成为

  private void ChannelFaulted(object sender, EventArgs e) { IClientChannel channel = (IClientChannel)sender; channel.Abort(); } 

似乎允许最初的例外起泡。 还select不使用channel.close,因为通道已经处于故障状态,似乎会引发exception。 FactoryFaulted事件处理程序可能有类似的问题。 Btw @Darin,好一点的代码…