我喜欢在using
块中实例化我的WCF服务客户端,因为它几乎是使用实现IDisposable
资源的标准方法:
using (var client = new SomeWCFServiceClient())
{
//Do something with the client
}
但是,正如本MSDN文章中所述 ,在using
块中包装WCF客户端可能会掩盖导致客户端处于故障状态的任何错误(如超时或通信问题)。 简而言之,当调用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
块而没有掩盖故障状态异常的危险。
那么,我有什么其他的问题需要注意使用这些变通方法吗? 有没有人想出更好的东西?
#1楼
根据Marc Gravell,MichaelGG和Matt Davis的回答,我们的开发人员提出了以下建议:
public static class UsingServiceClient
{
public static void Do<TClient>(TClient client, Action<TClient> execute)
where TClient : class, ICommunicationObject
{
try
{
execute(client);
}
finally
{
client.DisposeSafely();
}
}
public static void DisposeSafely(this ICommunicationObject client)
{
if (client == null)
{
return;
}
bool success = false;
try
{
if (client.State != CommunicationState.Faulted)
{
client.Close();
success = true;
}
}
finally
{
if (!success)
{
client.Abort();
}
}
}
}
使用示例:
string result = string.Empty;
UsingServiceClient.Do(
new MyServiceClient(),
client =>
result = client.GetServiceResult(parameters));
它尽可能接近“using”语法,在调用void方法时不必返回虚值,并且可以多次调用服务(并返回多个值)而不必使用元组。
此外,如果需要,您可以使用ClientBase<T>
后代而不是ChannelFactory。
如果开发人员想要手动处理代理/通道,则会暴露扩展方法。
#2楼
考虑到IServiceOriented.com倡导的解决方案与David Barret博客倡导的解决方案之间的选择,我更喜欢通过覆盖客户端的Dispose()方法提供的简单性。 这允许我继续使用using()声明,就像一个一次性对象所期望的那样。 但是,正如@Brian指出的那样,这个解决方案包含一个竞争条件,因为状态在检查时可能不会出现故障,但可能在调用Close()时,在这种情况下仍然会发生CommunicationException。
因此,为了解决这个问题,我采用了一种混合了两全其美的解决方案。
void IDisposable.Dispose()
{
bool success = false;
try
{
if (State != CommunicationState.Faulted)
{
Close();
success = true;
}
}
finally
{
if (!success)
Abort();
}
}
#3楼
我写了一个简单的基类来处理这个问题。 它可以作为NuGet包使用 ,而且非常易于使用。
//MemberServiceClient is the class generated by SvcUtil
public class MemberServiceManager : ServiceClientBase<MemberServiceClient>
{
public User GetUser(int userId)
{
return PerformServiceOperation(client => client.GetUser(userId));
}
//you can also check if any error occured if you can't throw exceptions
public bool TryGetUser(int userId, out User user)
{
return TryPerformServiceOperation(c => c.GetUser(userId), out user);
}
}
#4楼
@Marc Gravell
使用它不是没关系的:
public static TResult Using<T, TResult>(this T client, Func<T, TResult> work)
where T : ICommunicationObject
{
try
{
var result = work(client);
client.Close();
return result;
}
catch (Exception e)
{
client.Abort();
throw;
}
}
或者,在Service<IOrderService>.Use
情况下,同样的事情(Func<T, TResult>)
这些将使返回变量更容易。
#5楼
public static class Service<TChannel>
{
public static ChannelFactory<TChannel> ChannelFactory = new ChannelFactory<TChannel>("*");
public static TReturn Use<TReturn>(Func<TChannel,TReturn> codeBlock)
{
var proxy = (IClientChannel)ChannelFactory.CreateChannel();
var success = false;
try
{
var result = codeBlock((TChannel)proxy);
proxy.Close();
success = true;
return result;
}
finally
{
if (!success)
{
proxy.Abort();
}
}
}
}
所以它允许很好地编写return语句:
return Service<IOrderService>.Use(orderService =>
{
return orderService.PlaceOrder(request);
});
来源:oschina
链接:https://my.oschina.net/stackoom/blog/3164326