WCF ChannelFactory和通道-缓存、重用、关闭和恢复

WCF ChannelFactory和通道-缓存、重用、关闭和恢复,wcf,dispose,channel,channelfactory,recover,Wcf,Dispose,Channel,Channelfactory,Recover,我为我的WCF客户端库规划了以下体系结构: 使用ChannelFactory而不是svcutil生成代理,因为 我需要更多的控制,而且我想让客户在一个单独的 汇编并避免在我的WCF服务更改时重新生成 需要将带有消息检查器的行为应用于我的WCF 端点,因此每个通道都能够发送其 自有认证令牌 我的客户端库将从MVC前端使用,因此我必须考虑可能的线程问题 我正在使用.NET4.5(也许它有一些帮助工具或新方法可以更好地实现WCF客户端?) 我已经读了很多关于各种独立位的文章,但我仍然不知道如何以正

我为我的WCF客户端库规划了以下体系结构:

  • 使用ChannelFactory而不是svcutil生成代理,因为 我需要更多的控制,而且我想让客户在一个单独的 汇编并避免在我的WCF服务更改时重新生成
  • 需要将带有消息检查器的行为应用于我的WCF 端点,因此每个通道都能够发送其 自有认证令牌
  • 我的客户端库将从MVC前端使用,因此我必须考虑可能的线程问题
  • 我正在使用.NET4.5(也许它有一些帮助工具或新方法可以更好地实现WCF客户端?)
我已经读了很多关于各种独立位的文章,但我仍然不知道如何以正确的方式将它们组合在一起。我有以下问题:

  • 据我所知,建议在静态变量中缓存ChannelFactory,然后从中取出通道,对吗
  • 端点行为是否特定于整个ChannelFactory,或者我可以分别为每个通道应用身份验证行为?如果行为特定于整个工厂,这意味着我不能在端点行为对象中保留任何状态信息,因为相同的身份验证令牌将被每个通道重用,但显然我希望每个通道都有其自己的当前用户身份验证令牌。这意味着,我必须计算端点行为中的令牌(我可以将其保存在HttpContext中,而我的消息检查器行为只会将其添加到传出消息中)
  • 我的客户机类是一次性的(实现IDispose)。知道通道可能处于任何可能的状态(未打开、已打开、已失败…),如何正确处理通道?我要把它处理掉吗?我是否要中止它,然后进行处置?我是否关闭它(但它可能根本还没有打开)然后进行处置
  • 如果在使用频道时出现故障,我该怎么办?是只有通道断了还是整个通道工厂都断了
  • 我猜,一行代码可以说一千多个单词,所以这里是我的代码形式的想法。我已经在代码中用“?”标记了上述所有问题

    public class MyServiceClient : IDisposable
    {
        // channel factory cache
        private static ChannelFactory<IMyService> _factory;
        private static object _lock = new object();
    
        private IMyService _client = null;
        private bool _isDisposed = false;
    
         /// <summary>
        /// Creates a channel for the service
        /// </summary>
        public MyServiceClient()
        {
            lock (_lock)
            {
                if (_factory == null)
                {
                    // ... set up custom bindings here and get some config values
    
                    var endpoint = new EndpointAddress(myServiceUrl);
                    _factory = new ChannelFactory<IMyService>(binding, endpoint);
    
                    // ???? do I add my auth behavior for entire ChannelFactory 
                    // or I can apply it for individual channels when I create them?
                }
            }
    
            _client = _factory.CreateChannel();
        }
    
        public string MyMethod()
        {
            RequireClientInWorkingState();
            try
            {
                return _client.MyMethod();
            }
            catch
            {
                RecoverFromChannelFailure();
                throw;
            }
        }
    
        private void RequireClientInWorkingState()
        {
            if (_isDisposed)
                throw new InvalidOperationException("This client was disposed. Create a new one.");
    
            // ??? is it enough to check for CommunicationState.Opened && Created?
            if (state != CommunicationState.Created && state != CommunicationState.Opened)
                throw new InvalidOperationException("The client channel is not ready to work. Create a new one.");
        }
    
        private void RecoverFromChannelFailure()
        {
            // ??? is it the best way to check if there was a problem with the channel?
            if (((IChannel)_client).State != CommunicationState.Opened)
            {
                // ??? is it safe to call Abort? won't it throw?
                ((IChannel)_client).Abort();
            }
    
            // ??? and what about ChannelFactory? 
            // will it still be able to create channels or it also might be broken and must be thrown away? 
            // In that case, how do I clean up ChannelFactory correctly before creating a new one?
        }
    
        #region IDisposable
    
        public void Dispose()
        {    
            // ??? is it how to free the channel correctly?
            // I've heard, broken channels might throw when closing 
            // ??? what if it is not opened yet?
            // ??? what if it is in fault state?
            try
            {
                ((IChannel)_client).Close();
            }
            catch
            {
               ((IChannel)_client).Abort();              
            }
    
            ((IDisposable)_client).Dispose();
    
            _client = null;
            _isDisposed = true;
        }
    
        #endregion
    }
    
    公共类MyServiceClient:IDisposable
    {
    //通道工厂缓存
    私人静态通道工厂(u工厂),;
    私有静态对象_lock=新对象();
    私有IMyService_client=null;
    私有bool_isDisposed=false;
    /// 
    ///为服务创建一个通道
    /// 
    公共MyServiceClient()
    {
    锁
    {
    如果(_factory==null)
    {
    //…在此处设置自定义绑定并获取一些配置值
    var endpoint=新的端点地址(myServiceUrl);
    _工厂=新通道工厂(绑定,端点);
    //我是否为整个ChannelFactory添加身份验证行为
    //或者我可以在创建单个频道时将其应用于它们?
    }
    }
    _客户端=_factory.CreateChannel();
    }
    公共字符串MyMethod()
    {
    RequireClientInWorkingState();
    尝试
    {
    返回_client.MyMethod();
    }
    抓住
    {
    RecoverFromChannelFailure();
    投掷;
    }
    }
    私有void RequireClientInWorkingState()
    {
    如果(_isDisposed)
    抛出新的InvalidOperationException(“此客户端已被释放。创建一个新客户端”);
    //?检查通信状态是否已打开和创建就足够了?
    if(state!=CommunicationState.Created&&state!=CommunicationState.Opened)
    抛出新的InvalidOperationException(“客户端通道未准备好工作。请创建一个新通道”);
    }
    private void RecoverFromChannelFailure()
    {
    //??这是检查通道是否存在问题的最佳方法吗?
    if(((IChannel)\u client.State!=通信状态.Opened)
    {
    //?调用中止安全吗?它不会抛出吗?
    ((IChannel)u client.Abort();
    }
    //??那么ChannelFactory呢?
    //它是否仍然能够创建频道,或者它也可能被破坏,必须扔掉?
    //在这种情况下,如何在创建新的ChannelFactory之前正确清理ChannelFactory?
    }
    #可识别区域
    公共空间处置()
    {    
    //??如何正确地释放通道?
    //我听说,关闭时坏掉的频道可能会抛出
    //??如果它还没有打开怎么办?
    //??如果它处于故障状态怎么办?
    尝试
    {
    ((IChannel)u client.Close();
    }
    抓住
    {
    ((IChannel)u client.Abort();
    }
    ((IDisposable)u client.Dispose();
    _client=null;
    _isDisposed=true;
    }
    #端区
    }
    
    我想晚一点总比不。。。看起来作者已经让它工作了,这可能会帮助未来的WCF用户

    1) ChannelFactory排列通道,其中包括通道的所有行为。通过CreateChannel方法创建通道“激活”通道。可以缓存通道工厂

    2) 您可以使用绑定和行为来塑造通道工厂。此形状与创建此频道的所有人共享。正如您在评论中所指出的,您可以附加消息检查器,但更常见的情况是使用头向服务发送自定义状态信息。您可以通过OperationContext.Current附加标题

    using (var op = new OperationContextScope((IContextChannel)proxy))
    {
        var header = new MessageHeader<string>("Some State");
        var hout = header.GetUntypedHeader("message", "urn:someNamespace");
        OperationContext.Current.OutgoingMessageHeaders.Add(hout);
    }
    
    4) WCF将故障通道,而不是工厂。您可以实现重新连接逻辑,但这需要您从一些自定义ProxyBase(例如

    protected I Channel
    {
        get
        {
            lock (_channelLock)
            {
                if (! object.Equals(innerChannel, default(I)))
                {
                    ICommunicationObject channelObject = innerChannel as ICommunicationObject;
                    if ((channelObject.State == CommunicationState.Faulted) || (channelObject.State == CommunicationState.Closed))
                    {
                        // Channel is faulted or closing for some reason, attempt to recreate channel
                        innerChannel = default(I);
                    }
                }
    
                if (object.Equals(innerChannel, default(I)))
                {
                    Debug.Assert(Factory != null);
                    innerChannel = Factory.CreateChannel();
                    ((ICommunicationObject)innerChannel).Faulted += new EventHandler(Channel_Faulted);
                }
            }
    
            return innerChannel;
        }
    }
    
    5) 不要重复使用频道。打开、做某事、关闭是正常的使用模式

    6) 创建公共代理基类并从中派生所有客户端。这会很有帮助,比如使用pre-invoke/po重新连接
    protected I Channel
    {
        get
        {
            lock (_channelLock)
            {
                if (! object.Equals(innerChannel, default(I)))
                {
                    ICommunicationObject channelObject = innerChannel as ICommunicationObject;
                    if ((channelObject.State == CommunicationState.Faulted) || (channelObject.State == CommunicationState.Closed))
                    {
                        // Channel is faulted or closing for some reason, attempt to recreate channel
                        innerChannel = default(I);
                    }
                }
    
                if (object.Equals(innerChannel, default(I)))
                {
                    Debug.Assert(Factory != null);
                    innerChannel = Factory.CreateChannel();
                    ((ICommunicationObject)innerChannel).Faulted += new EventHandler(Channel_Faulted);
                }
            }
    
            return innerChannel;
        }
    }
    
    public static void SetTimeouts(Binding binding, TimeSpan? timeout = null, TimeSpan? debugTimeout = null)
            {
                if (timeout == null)
                {
                    timeout = new TimeSpan(0, 0, 1, 0);
                }
                if (debugTimeout == null)
                {
                    debugTimeout = new TimeSpan(0, 0, 10, 0);
                }
                if (Debugger.IsAttached)
                {
                    binding.ReceiveTimeout = debugTimeout.Value;
                    binding.SendTimeout = debugTimeout.Value;
                }
                else
                {
                    binding.ReceiveTimeout = timeout.Value;
                    binding.SendTimeout = timeout.Value;
                }
            }