Asp.net mvc 处置WCF代理的正确方法是什么?

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,从而使问题变得模糊 然而,造成混淆的最大原因是,在出

我一直在努力与WCF代理。处置WCF代理的正确方法是什么?答案并非微不足道

System.ServiceModel.ClientBase违反了Microsoft自己的处置模式

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()
{
这