【问题标题】:Error handling in ChannelFactory vs auto-generated proxiesChannelFactory 与自动生成的代理中的错误处理
【发布时间】:2012-11-13 16:32:29
【问题描述】:

我最近开始编写自己的 ChannelFactory 包装器来调用我的 WCF 服务,而不是依赖于 Visual Studio 自动生成的代码

以前,使用自动生成的代理,我可以这样做以进行错误检查:

    protected AccessControlServiceClient AccessControlService
    {
        get
        {
            // Check if not initialized yet
            if (HttpContext.Current.Session["AccessControlServiceClient"] == null)
                HttpContext.Current.Session["AccessControlServiceClient"] = new AccessControlServiceClient();

            // If current client is 'faulted' (due to some error), create a new instance.
            var client = HttpContext.Current.Session["AccessControlServiceClient"] as AccessControlServiceClient;
            if (client.State == CommunicationState.Faulted)
            {
                try { client.Abort(); }
                catch { /* no action */ }

                client = new AccessControlServiceClient();
                HttpContext.Current.Session["AccessControlServiceClient"] = client;
            }

            return client;
        }
    }

对于 ChannelFactor,我应该如何处理这个问题?如果出现问题,处理和重新创建频道的最佳做法是什么?例如,断断续续的网络连接、会话超时等。

这是我的代码目前的样子:

来自 ProxyBase.cs 的片段 - 创建通道

private void Initialise()
{
    lock (_sync)
    {
        if (_channel != null) return;

        _channel = new ChannelFactory<T>(_serviceEndPointUri).CreateChannel();
    }
 }

UserManagementServiceClient.cs - IUserManagementService 是 WCF 合同

public class UserManagementServiceClient : ProxyBase<IUserManagementService>
{
    public UserManagementServiceClient(string serviceEndPointUri)
        : base(serviceEndPointUri)
    {
    }

    public TokenResponse GetToken(TokenRequest request)
    {            
        return Channel.GetToken(request);
    }


    public LoginResponse Login(LoginRequest request)
    {
        return Channel.Login(request);
    }


    public LogoutResponse Logout(LogoutRequest request)
    {
        return Channel.Logout(request);
    }
}

最后,这就是我在 MVC 项目中的调用方式

    protected UserManagementServiceClient UserManagementService
    {
        get
        {
            // Check if not initialized yet
            if (HttpContext.Current.Session["UserManagementServiceClient"] == null)
                HttpContext.Current.Session["UserManagementServiceClient"] = new UserManagementServiceClient("NetTcpBinding_UserManagementService");

            var client = HttpContext.Current.Session["UserManagementServiceClient"] as UserManagementServiceClient;

            return client;
        }
    }

所以目前,每当默认的 10 分钟会话结束时,我都会收到错误消息,因为频道已经关闭。

如何强化我的代码,使其能够处理网络断开/会话超时等问题?

【问题讨论】:

    标签: c# .net wcf proxy channelfactory


    【解决方案1】:

    我使用这样的代码

    public T Channel {
        get {
            lock (_channelLock) {
                if (!object.Equals(_channel, default(T))) {
                    if (((ICommunicationObject)_channel).State == CommunicationState.Faulted) {
                        // channel has been faulted, we want to create a new one so clear it
                        _channel = default(T);
                    }
                }
    
                if (object.Equals(_channel, default(T))) {
                    // channel is null, create a new one
                    Debug.Assert(_channelFactory != null);
                    _channel = _channelFactory.CreateChannel();
                }
                return _channel;
            }
       }
    

    【讨论】:

      【解决方案2】:

      我通常在每个操作中使用一个新的服务实例。像这样:

      public async Task<bool> IsOnline()
          {
              using (var service = new DebugService())
              {
                  return await service.OnlineCheckAsync();
              }
          }
      

      除其他外,我的包装器在this article 之后实现了 IDisposable。 这样就不用担心每次操作时通道是否有故障。

      编辑

      IDisposable 实现的原始链接已损坏,但代码如下所示:

      public void Dispose()
          {
              // The following code is from: http://www.vasylevskyi.com/2010/11/correct-wcf-client-proxy-closing.html
              try
              {
                  if (this.State != CommunicationState.Closed && this.State != CommunicationState.Faulted)
                  {
                      ((ICommunicationObject)this).BeginClose(
                          (asr) =>
                          {
                              try
                              {
                                  ((ICommunicationObject)this).EndClose(asr);
                              }
                              catch
                              {
                                  this.Abort();
                              }
                          }, null
                      );
                  }
                  else
                  {
                      this.Abort();
                  }
              }
              catch (CommunicationException)
              {
                  this.Abort();
              }
      

      【讨论】:

      • 确实,我在编辑中突出显示了它。不过,链接中可用的代码现在在答案中。
      【解决方案3】:

      在我当前的项目中,我为客户端创建了一个包装类,它使用CurrentInstance 访问器模型。 getter 检查_client.State 中的ClosingClosedFaulted,并在这种情况下创建一个新客户端。创建新客户端是由提供给包装器构造函数的工厂委托完成的,因此有一种免费但一致的方式来创建客户端实例。

      对于您的场景,这意味着将包装器存储在 Session 中,而不是直接存储客户端。然后调用wrapper.CurrentInstance 将检查状态,如果在此期间发生超时,则在后台重新创建客户端。

      这个模型还可以通过强大的错误处理(物理断开连接、服务器崩溃......)、延迟初始化、创建新客户端后的任意初始化逻辑(在我的例子中它调用某种Subscribe 方法)来方便地扩展创建一个回调通道,但它可以是任何东西)

      我希望这对你来说也是正确的方向。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-10
        • 2011-03-18
        • 1970-01-01
        • 2010-09-26
        • 1970-01-01
        • 2021-05-14
        相关资源
        最近更新 更多