WCF 客户端“使用"块问题的最佳解决方法是什么? [英] What is the best workaround for the WCF client `using` block issue?
问题描述
我喜欢在 using
块中实例化我的 WCF 服务客户端,因为这几乎是使用实现 IDisposable
的资源的标准方式:
I like instantiating my WCF service clients within a using
block as it's pretty much the standard way to use resources that implement IDisposable
:
using (var client = new SomeWCFServiceClient())
{
//Do something with the client
}
但是,如这篇 MSDN 文章所述,包装了一个using
块中的 WCF 客户端可以屏蔽导致客户端处于故障状态(如超时或通信问题)的任何错误.长话短说,当调用 Dispose()
时,客户端的 Close()
方法会触发,但会抛出错误,因为它处于错误状态.然后原始异常被第二个异常屏蔽.不好.
But, as noted in this MSDN article, wrapping a WCF client in a using
block could mask any errors that result in the client being left in a faulted state (like a timeout or communication problem). Long story short, when Dispose()
is called, the client's Close()
method fires, but throws an error because it's in a faulted state. The original exception is then masked by the second exception. Not good.
MSDN 文章中建议的解决方法是完全避免使用 using
块,而是实例化您的客户端并像这样使用它们:
The suggested workaround in the MSDN article is to completely avoid using a using
block, and to instead instantiate your clients and use them something like this:
try
{
...
client.Close();
}
catch (CommunicationException e)
{
...
client.Abort();
}
catch (TimeoutException e)
{
...
client.Abort();
}
catch (Exception e)
{
...
client.Abort();
throw;
}
与 using
块相比,我认为这很丑陋.每次需要客户端时都要编写大量代码.
Compared to the using
block, I think that's ugly. And a lot of code to write each time you need a client.
幸运的是,我找到了其他一些解决方法,例如(现已不存在的)IServiceOriented 博客上的这个.你开始:
Luckily, I found a few other workarounds, such as this one on the (now defunct) IServiceOriented blog. You start with:
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
块那样具有表现力和易于理解.
That's not bad, but I don't think it's as expressive and easily understandable as the using
block.
我目前尝试使用的解决方法我首先在 blog.davidbarret 上阅读.net.基本上,无论您在哪里使用它,都可以覆盖客户端的 Dispose()
方法.类似的东西:
The workaround I'm currently trying to use I first read about on blog.davidbarret.net. Basically, you override the client's Dispose()
method wherever you use it. Something like:
public partial class SomeWCFServiceClient : IDisposable
{
void IDisposable.Dispose()
{
if (this.State == CommunicationState.Faulted)
{
this.Abort();
}
else
{
this.Close();
}
}
}
这似乎能够再次允许 using
块,而不会掩盖错误状态异常的危险.
This appears to be able to allow the using
block again without the danger of masking a faulted state exception.
那么,在使用这些变通方法时,我还需要注意其他问题吗?有没有人想出更好的办法?
So, are there any other gotchas I have to look out for using these workarounds? Has anybody come up with anything better?
推荐答案
其实,虽然我 博客(见卢克的回答),我认为 这个 比我的 IDisposable 包装器更好.典型代码:
Actually, although I blogged (see Luke's answer), I think this is better than my IDisposable wrapper. Typical code:
Service<IOrderService>.Use(orderService=>
{
orderService.PlaceOrder(request);
});
<小时>
(按评论编辑)
(edit per comments)
由于 Use
返回 void,因此处理返回值的最简单方法是通过捕获的变量:
Since Use
returns void, the easiest way to handle return values is via a captured variable:
int newOrderId = 0; // need a value for definite assignment
Service<IOrderService>.Use(orderService=>
{
newOrderId = orderService.PlaceOrder(request);
});
Console.WriteLine(newOrderId); // should be updated
这篇关于WCF 客户端“使用"块问题的最佳解决方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!