[英]creating WCF ChannelFactory<T>
I'm trying to convert an existing .NET Remoting application to WCF. 我正在尝试将现有的.NET Remoting应用程序转换为WCF。 Both server and client share common interface and all objects are server-activated objects.
服务器和客户端共享公共接口,所有对象都是服务器激活的对象。
In WCF world, this would be similar to creating per-call service and using ChannelFactory<T>
to create a proxy. 在WCF世界中,这类似于创建每个呼叫服务并使用
ChannelFactory<T>
来创建代理。 I'm struggling a bit with how to properly create ChannelFactory<T>
for an ASP.NET client. 我正在努力解决如何为ASP.NET客户端正确创建
ChannelFactory<T>
的问题。
For performance reasons, I want to cache ChannelFactory<T>
objects and just create channel every time I call the service. 出于性能原因,我想缓存
ChannelFactory<T>
对象,并在每次调用服务时创建通道。 In .NET remoting days, there used to be RemotingConfiguration.GetRegisteredWellknownClientTypes()
method to get a collection of client objects that I could then cache. 在.NET远程处理时代,曾经有过
RemotingConfiguration.GetRegisteredWellknownClientTypes()
方法来获取我可以缓存的客户端对象的集合。 It appears, in WCF world there is no such thing, although I was able to get a collection of endpoints from config file. 看来,在WCF世界中没有这样的东西,虽然我能够从配置文件中获取端点集合。
Now here is what I think will work. 现在我认为这将是有效的。 I can create something like this:
我可以创建这样的东西:
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;
}
}
I think the above code will work, but I'm a bit worried about multiple threads trying to add new ChannelFactory<T>
objects if it's not in the lookup. 我认为上面的代码可以工作,但我有点担心多个线程试图添加新的
ChannelFactory<T>
对象,如果它不在查找中。 Since I'm using .NET 4.0, I was thinking about using ConcurrentDictionary
and use GetOrAdd()
method or use TryGetValue()
method first to check if ChannelFactory<T>
exists and it does not exist, then use GetOrAdd()
method. 由于我使用的是.NET 4.0,我考虑使用
ConcurrentDictionary
并使用GetOrAdd()
方法或首先使用TryGetValue()
方法来检查ChannelFactory<T>
存在且它不存在,然后使用GetOrAdd()
方法。 Not sure about performance though of ConcurrentDictionary.TryGetValue()
and ConcurrentDictionary.GetOrAdd()
method. 不确定
ConcurrentDictionary.TryGetValue()
和ConcurrentDictionary.GetOrAdd()
方法的性能。
Another minor question is whether I need to call ChannelFactory.Close()
method on channel factory objects after ASP.NET application ends or can I just let .NET framework dispose the channel factory objects on its own. 另一个小问题是我是否需要在ASP.NET应用程序结束后在通道工厂对象上调用
ChannelFactory.Close()
方法,或者我可以让.NET框架自己配置通道工厂对象。 The proxy channel will always be closed after calling service method by using ((IChannel)proxy).Close()
method. 通过使用
((IChannel)proxy).Close()
方法调用服务方法后,代理通道将始终关闭。
Here's a helper class that I use to handle channel factories: 这是我用来处理通道工厂的辅助类:
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();
}
}
}
}
Then I define a service invoker: 然后我定义了一个服务调用者:
public interface IServiceInvoker
{
R InvokeService<T, R>(Func<T, R> invokeHandler) where T: class;
}
and an implementation: 和实施:
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;
}
}
Now every time you need to call a WCF service you could use this: 现在每次需要调用WCF服务时都可以使用:
WCFServiceInvoker invoker = new WCFServiceInvoker();
SomeReturnType result = invoker.InvokeService<IMyServiceContract, SomeReturnType>(
proxy => proxy.SomeMethod()
);
This assumes that you've defined a client endpoint for the IMyServiceContract
service contract in the config file: 这假设您已在配置文件中为
IMyServiceContract
服务合同定义了客户端端点:
<client>
<endpoint
name="myservice"
address="http://example.com/"
binding="basicHttpBinding"
contract="IMyServiceContract" />
</client>
Yes, if you want to create something like this - a static class to hold all those ChannelFactory<T>
instances - you definitely have to make sure this class is 100% thread-safe and cannot stumble when accessed concurrently. 是的,如果你想创建这样的东西 - 一个静态类来保存所有那些
ChannelFactory<T>
实例 - 你必须确保这个类是100%线程安全的,并且在并发访问时不会绊倒。 I haven't used .NET 4's features much yet, so I cannot comment on those specifically - but I would definitely recommend to make this as safe as possible. 我还没有使用.NET 4的功能,所以我不能对这些功能发表评论 - 但我肯定会建议尽可能安全。
As for your second (minor) question: the ChannelFactory itself is a static class - so you cannot really call a .Close()
method on it. 至于你的第二个(次要)问题:ChannelFactory本身是一个静态类 - 所以你不能真正调用它上面的
.Close()
方法。 If you meant to ask whether or not to call the .Close()
method on the actual IChannel
, then again: yes, try your best to be a good citizen and close those channels if you ever can. 如果您打算询问是否在实际的
IChannel
上调用.Close()
方法,那么请再次:是的,尽量做一个好公民并尽可能关闭这些渠道。 If you miss one, .NET will take care of it - but don't just toss your unused channels on the floor and go on - clean up after yourself! 如果你错过了一个,.NET会照顾它 - 但不要只是把你未使用的频道丢在地板上继续 - 自己清理干净! :-)
:-)
I didn't like the calling construction: 我不喜欢呼叫结构:
WCFServiceInvoker invoker = new WCFServiceInvoker();
var result = invoker.InvokeService<IClaimsService, ICollection<string>>(proxy => proxy.GetStringClaims());
Also you cannot use the same channel twice. 此外,您不能两次使用相同的频道。
I've created this solution: 我创建了这个解决方案:
using(var i = Connection<IClaimsService>.Instance)
{
var result = i.Channel.GetStringClaims();
}
Now you can reuse the same channel until the using statement calls the dispose. 现在,您可以重用相同的通道,直到using语句调用dispose。
The GetChannel method is basicly a ChannelFactory.CreateChannel() with some extra config's I'm using. GetChannel方法基本上是一个ChannelFactory.CreateChannel(),我正在使用一些额外的配置。
You could build some caching for the ChannelFactory's as the other solutions does. 您可以像其他解决方案那样为ChannelFactory构建一些缓存。
Code for the Connnection class: 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, yes I went down the road of not using a try catch in the ChannelFactoryManager ChannelFaulted event handler. @NelsonRothermel,是的,我在ChannelFactoryManager ChannelFaulted事件处理程序中没有使用try catch。 So ChannelFaulted would become
所以ChannelFaulted会成为
private void ChannelFaulted(object sender, EventArgs e)
{
IClientChannel channel = (IClientChannel)sender;
channel.Abort();
}
Seems to allow the original exception to bubble up. 似乎允许原始异常冒泡。 Also chose not to use channel.close as it seems to throw an exception as the channel is in a faulted state already.
还选择不使用channel.close,因为它似乎已抛出异常,因为通道已经处于故障状态。 FactoryFaulted event handler may have similar issues.
FactoryFaulted事件处理程序可能有类似的问题。 Btw @Darin, good bit of code...
Btw @Darin,代码很好......
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.