Asp.net mvc 处置WCF代理的正确方法是什么?
我一直在努力与WCF代理。处置WCF代理的正确方法是什么?答案并非微不足道 System.ServiceModel.ClientBase违反了Microsoft自己的处置模式Asp.net mvc 处置WCF代理的正确方法是什么?,asp.net-mvc,wcf,dispose,nettcpbinding,realproxy,Asp.net Mvc,Wcf,Dispose,Nettcpbinding,Realproxy,我一直在努力与WCF代理。处置WCF代理的正确方法是什么?答案并非微不足道 System.ServiceModel.ClientBase违反了Microsoft自己的处置模式 System.ServiceModel.ClientBase确实实现了IDisposable,因此必须假设它应该在中使用-block进行处置或使用。这些是任何一次性产品的最佳实践。但是,该实现是显式的,因此必须显式地将ClientBase实例转换为IDisposable,从而使问题变得模糊 然而,造成混淆的最大原因是,在出
System.ServiceModel.ClientBase
确实实现了IDisposable
,因此必须假设它应该在中使用-block进行处置或使用。这些是任何一次性产品的最佳实践。但是,该实现是显式的,因此必须显式地将ClientBase
实例转换为IDisposable
,从而使问题变得模糊
然而,造成混淆的最大原因是,在出现故障的ClientBase
实例上调用Dispose()
,甚至是因为从未打开而出现故障的通道,都会导致引发异常。这不可避免地意味着,当堆栈展开时,解释故障的有意义异常会立即丢失,using
作用域结束,Dispose()
抛出一个无意义异常,表示无法处理故障通道
上述行为与dispose模式背道而驰,该模式规定对象必须能够容忍对dispose()
的多个显式调用。(请参阅,“…允许多次调用Dispose(bool)
方法。该方法可能会在第一次调用后选择不执行任何操作。”)
随着控制反转的出现,这种糟糕的实现成为了一个现实问题。I.O.C.容器(特别是Ninject)检测IDisposable
接口,并在注入作用域末尾的激活实例上显式调用Dispose()
解决方案:代理ClientBase并拦截对Dispose()的调用。
我的解决方案是通过子类化System.Runtime.Remoting.proxy.RealProxy来代理ClientBase
,并劫持或拦截对Dispose()
的调用。我对Dispose()
的第一次替换如下:
if (_client.State == CommunicationState.Faulted) _client.Abort();
else ((IDisposable)_client).Dispose();
(请注意,\u client
是对代理目标的类型化引用。)
NetTcpBinding的问题
起初我认为这已经解决了问题,但后来我在生产中发现了一个问题:在某些极难复制的场景下,我发现使用NetTcpBinding
的频道在未出现故障的情况下没有正确关闭,即使在\u客户机上调用了Dispose
我有一个ASP.NET MVC应用程序,它使用我的代理实现,使用本地网络上的NetTcpBinding
连接到WCF服务,托管在一个只有一个节点的服务群集上的Windows NT服务中。当我对MVC应用程序进行负载测试时,WCF服务(使用端口共享)上的某些端点会在一段时间后停止响应
我努力复制这一点:在两台开发人员机器之间的局域网上运行的相同组件工作得很好;一个控制台应用程序使用多个进程和多个线程来敲打真实的WCF端点(在临时服务集群上运行);在暂存服务器上配置MVC应用程序,以连接到在负载下工作的开发人员机器上的端点;在开发人员的机器上运行MVC应用程序并连接到暂存WCF端点是可行的。然而,最后一个场景仅在IIS Express下工作,这是一个突破。当在开发人员的机器上,连接到登台服务集群,在全脂IIS下对MVC应用程序进行负载测试时,端点会死机
解决方案:关闭频道
在未能理解问题并阅读了许许多多MSDN页面和其他声称问题根本不存在的来源后,我尝试了一个长期的尝试,并将我的Dispose()
解决方案更改为
if (_client.State == CommunicationState.Faulted) _client.Abort();
else if (_client.State == CommunicationState.Opened)
{
((IContextChannel)_client.Channel).Close();
((IDisposable)_client).Dispose();
}
else ((IDisposable)_client).Dispose();
。。。并且,在所有测试设置中以及在暂存环境中处于负载状态时,问题不再发生
为什么?
有人能解释一下可能发生了什么,以及为什么在调用Dispose()
之前显式关闭频道
?据我所知,这是不必要的
最后,我回到开始的问题:处理WCF代理的正确方法是什么?我对Dispose()
的替换是否足够?据我所知,问题是调用Dispose
会取消句柄,但实际上不会关闭频道,而频道会保留资源,最终超时
这就是为什么您的服务在负载测试一段时间后停止响应的原因:因为初始调用占用资源的时间比您想象的要长,随后的调用无法利用这些资源
我提出了以下解决方案。该解决方案的前提是调用Dispose
应该足以处理掉句柄并关闭通道。另一个好处是,如果客户端最终处于故障状态,则会重新创建它,以便后续调用成功
如果通过依赖项注入框架(如Ninject
)将ServiceClient
注入到另一个类中,那么所有资源都将正确释放
注意:请注意,在Ninject
的情况下,绑定必须定义一个作用域,即它不能缺少InXyzScope
或使用内部作用域定义。如果没有作用域,则使用InCallScope
以下是我的想法:
public class ServiceClient<TService> : IDisposable
{
private readonly ChannelFactory<TService> channelFactory;
private readonly Func<TService> createChannel;
private Lazy<TService> service;
public ServiceClient(ChannelFactory<TService> channelFactory)
: base()
{
this.channelFactory = channelFactory;
this.createChannel = () =>
{
var channel = ChannelFactory.CreateChannel();
return channel;
};
this.service = new Lazy<TService>(() => CreateChannel());
}
protected ChannelFactory<TService> ChannelFactory
{
get
{
return this.channelFactory;
}
}
protected Func<TService, bool> IsChannelFaulted
{
get
{
return (service) =>
{
var channel = service as ICommunicationObject;
if (channel == null)
{
return false;
}
return channel.State == CommunicationState.Faulted;
};
}
}
protected Func<TService> CreateChannel
{
get
{
return this.createChannel;
}
}
protected Action<TService> DisposeChannel
{
get
{
return (service) =>
{
var channel = service as ICommunicationObject;
if (channel != null)
{
switch (channel.State)
{
case CommunicationState.Faulted:
channel.Abort();
break;
case CommunicationState.Closed:
break;
default:
try
{
channel.Close();
}
catch (CommunicationException)
{
}
catch (TimeoutException)
{
}
finally
{
if (channel.State != CommunicationState.Closed)
{
channel.Abort();
}
}
break;
}
}
};
}
}
protected Action<ChannelFactory<TService>> DisposeChannelFactory
{
get
{
return (channelFactory) =>
{
var disposable = channelFactory as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
};
}
}
public void Dispose()
{
DisposeChannel(this.service.Value);
DisposeChannelFactory(this.channelFactory);
}
public TService Service
{
get
{
if (this.service.IsValueCreated && IsChannelFaulted(this.service.Value))
{
DisposeChannel(this.service.Value);
this.service = new Lazy<TService>(() => CreateChannel());
}
return this.service.Value;
}
}
}
公共类服务客户端:IDisposable
{
私有只读ChannelFactory ChannelFactory;
私有只读Func createChannel;
私人懒惰服务;
公共服务客户端(ChannelFactory ChannelFactory)
:base()
{
这