WCF的ChannelFactory和渠道 - 缓存,重用,关闭和恢复(WCF ChannelFac

2019-08-08 10:48发布

我有我的WCF客户端库中的下列计划架构:

  • 使用的ChannelFactory代替SvcUtil工具生成的代理,因为我需要更多的控制,也是我想保持在一个单独的程序在客户端和避免当我WCF服务变更再生
  • 需要申请一个消息检查到我的WCF端点的行为,所以每个通道可以发送自己的身份验证令牌
  • 我的客户端库将从MVC前端使用,所以我得考虑可能的线程问题
  • 我使用.NET 4.5(也许它有一些助手或新的方法来实现WCF客户一些更好的办法?)

我看了一下各种不同的比特很多文章,但我仍然感到困惑如何把它放在一起以正确的方式。 我有以下问题:

  1. 按照我的理解,建议缓存的ChannelFactory在一个静态变量,然后获取渠道出来的,对吧?
  2. 为终结点行为具体到整个的ChannelFactory或我能将我的认证行为分开各个通道? 如果该行为是针对整个工厂,这意味着我不能让我的终结点行为对象的任何状态信息,因为同样的身份验证令牌将被重用为每一个通道,但很明显,我想每个通道都为自己的身份验证令牌当前用户。 这意味着,我必须来计算我的终结点行为的内部令牌(我可以把它在HttpContext的,我的消息检查的行为只会把它添加到传出消息)。
  3. 我的客户端类是一次性的(实现IDispose)。 如何正确处置渠道,知道它可能在任何可能的状态(不开张,开张,失败...)? 难道我只是处置呢? 难道我放弃它,然后处理? 难道我关闭它(但也可能是没有打开尚未在所有),然后处理?
  4. 与信道工作时,如果我得到了一些故障怎么办? 仅在渠道细分或全部的ChannelFactory被打破?

我想,一行代码讲一千多字,所以这里是我的代码形式的想法。 我已经导致了我的所有问题上面“???” 在代码中。

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
}

Answer 1:

我想亡羊补牢之后再也没有......,看起来像笔者有它的工作,这可能有助于未来的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);
}

3)这是我的设置客户端信道和工厂的一般方法(此方法是我ProxyBase类的一部分)

public virtual void Dispose()
{
    CloseChannel();
    CloseFactory();
}

protected void CloseChannel()
{
    if (((IChannel)_client).State == CommunicationState.Opened)
    {
        try
        {
            ((IChannel)_client).Close();
        }
        catch (TimeoutException /* timeout */)
        {
            // Handle the timeout exception
            ((IChannel)innerChannel).Abort();
        }
        catch (CommunicationException /* communicationException */)
        {
            // Handle the communication exception
            ((IChannel)_client).Abort();
        }
    }
}

protected void CloseFactory()
{
    if (Factory.State == CommunicationState.Opened)
    {
        try
        {
            Factory.Close();
        }
        catch (TimeoutException /* timeout */)
        {
            // Handle the timeout exception
            Factory.Abort();
        }
        catch (CommunicationException /* communicationException */)
        {
            // Handle the communication exception
            Factory.Abort();
        }
    }
}

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)建立普通代理基类,所有的客户从中获得。 这可以是有帮助的,如重新连接,使用调用预/后调用逻辑,消耗来自工厂事件(例如故障的,开口部)

7)创建自己的CustomChannelFactory这给你进一步控制工厂的行为如设置默认的超时,执行不同的绑定设置(MaxMessageSizes)等。

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;
            }
        }


文章来源: WCF ChannelFactory and channels - caching, reusing, closing and recovery