一尘不染

WCF客户端“使用”块问题的最佳解决方法是什么?

c#

我喜欢在一个using块中实例化WCF服务客户端,因为这几乎是使用实现资源的标准方法IDisposable

using (var client = new SomeWCFServiceClient()) 
{
    //Do something with the client 
}

但是,正如此MSDN文章中所述,将WCF客户端包装在一个using块中可能会掩盖导致客户端处于故障状态(如超时或通信问题)的任何错误。长话短说,当调用Dispose()时,客户端的Close()方法将触发,但由于处于故障状态而将引发错误。然后,原始异常被第二个异常掩盖。不好。

MSDN文章中建议的解决方法是完全避免使用using块,而是实例化客户端并使用如下所示的客户端:

try
{
    ...
    client.Close();
}
catch (CommunicationException e)
{
    ...
    client.Abort();
}
catch (TimeoutException e)
{
    ...
    client.Abort();
}
catch (Exception e)
{
    ...
    client.Abort();
    throw;
}

using块相比,我认为这很丑。每次需要客户时,都会编写很多代码。

幸运的是,我发现了其他一些解决方法,例如在IServiceOriented上的解决方法。您从开始:

public delegate void UseServiceDelegate<T>(T proxy);

public static class Service<T> 
{ 
    public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>("");

    public static void Use(UseServiceDelegate<T> codeBlock) 
    { 
        IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel(); 
        bool success = false; 
        try 
        { 
            codeBlock((T)proxy); 
            proxy.Close(); 
            success = true; 
        } 
        finally 
        { 
            if (!success) 
            { 
                proxy.Abort(); 
            } 
        } 
     } 
}

然后允许:

Service<IOrderService>.Use(orderService => 
{ 
    orderService.PlaceOrder(request); 
});

这还不错,但我认为它不像using街区那样具有表现力和易于理解。

我当前尝试使用的解决方法是,我首先在blog.davidbarret.net上进行了了解。基本上,Dispose()无论您在何处使用它,都将覆盖其方法。就像是:

public partial class SomeWCFServiceClient : IDisposable
{
    void IDisposable.Dispose() 
    {
        if (this.State == CommunicationState.Faulted) 
        {
            this.Abort();
        } 
        else 
        {
            this.Close();
        }
    }
}

这似乎能够using再次允许该块而没有掩盖故障状态异常的危险。

因此,使用这些解决方法是否还有其他陷阱?有没有人提出更好的建议?


阅读 230

收藏
2020-05-19

共1个答案

一尘不染

实际上,尽管我写了博客(请参阅Luke的答案),但我认为比我的IDisposable包装更好。典型代码:

Service<IOrderService>.Use(orderService=>
{
  orderService.PlaceOrder(request);
});

(按评论编辑)

由于Usereturn void,处理返回值的最简单方法是通过捕获的变量:

int newOrderId = 0; // need a value for definite assignment
Service<IOrderService>.Use(orderService=>
  {
    newOrderId = orderService.PlaceOrder(request);
  });
Console.WriteLine(newOrderId); // should be updated
2020-05-19