我喜欢在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
块,而不会掩盖故障状态异常。
所以,使用这些解决方法时还有其他需要注意的地方吗?有人想出了更好的解决办法吗?
Action<T>
替代UseServiceDelegate<T>
,这是一个次要的更改。 - hIpPyService<T>
,因为它会使单元测试变得复杂(就像大多数静态东西一样)。我更希望它是非静态的,这样它就可以被注入到正在使用它的类中。 - Fabio Marreco