【发布时间】:2013-01-10 21:44:41
【问题描述】:
我为我的 WCF 客户端库计划了以下架构:
- 使用 ChannelFactory 而不是 svcutil 生成代理,因为 我需要更多的控制权,而且我想把客户分开 组装并避免在我的 WCF 服务更改时重新生成
- 需要将带有消息检查器的行为应用到我的 WCF 端点,因此每个通道都能够发送其 自己的身份验证令牌
- 我的客户端库将从 MVC 前端使用,因此我必须考虑可能的线程问题
- 我正在使用 .NET 4.5(也许它有一些帮助程序或新方法可以更好地实现 WCF 客户端?)
我已经阅读了很多关于各种不同部分的文章,但我仍然对如何以正确的方式将它们组合在一起感到困惑。我有以下问题:
- 据我了解,建议将 ChannelFactory 缓存在静态变量中,然后从中取出通道,对吗?
- 是特定于整个 ChannelFactory 的端点行为还是我可以分别为每个通道应用我的身份验证行为?如果行为特定于整个工厂,这意味着我不能在端点行为对象中保留任何状态信息,因为相同的身份验证令牌将被每个通道重用,但显然我希望每个通道都有自己的身份验证令牌当前用户。这意味着,我必须在端点行为中计算令牌(我可以将它保存在 HttpContext 中,我的消息检查器行为只会将它添加到传出消息中)。
- 我的客户端类是一次性的(实现 IDispose)。我如何正确处置通道,知道它可能处于任何可能的状态(未打开、打开、失败......)?我只是丢弃它吗?我要中止它然后丢弃吗?我要关闭它(但它可能根本没有打开)然后丢弃吗?
- 如果我在使用频道时遇到问题怎么办?是只有通道坏了还是整个 ChannelFactory 坏了?
我猜,一行代码说一千多个单词,所以这里是我的代码形式的想法。我已经用“???”标记了我上面的所有问题在代码中。
public class MyServiceClient : IDisposable
{
// channel factory cache
private static ChannelFactory<IMyService> _factory;
private static object _lock = new object();
private IMyService _client = null;
private bool _isDisposed = false;
/// <summary>
/// Creates a channel for the service
/// </summary>
public MyServiceClient()
{
lock (_lock)
{
if (_factory == null)
{
// ... set up custom bindings here and get some config values
var endpoint = new EndpointAddress(myServiceUrl);
_factory = new ChannelFactory<IMyService>(binding, endpoint);
// ???? do I add my auth behavior for entire ChannelFactory
// or I can apply it for individual channels when I create them?
}
}
_client = _factory.CreateChannel();
}
public string MyMethod()
{
RequireClientInWorkingState();
try
{
return _client.MyMethod();
}
catch
{
RecoverFromChannelFailure();
throw;
}
}
private void RequireClientInWorkingState()
{
if (_isDisposed)
throw new InvalidOperationException("This client was disposed. Create a new one.");
// ??? is it enough to check for CommunicationState.Opened && Created?
if (state != CommunicationState.Created && state != CommunicationState.Opened)
throw new InvalidOperationException("The client channel is not ready to work. Create a new one.");
}
private void RecoverFromChannelFailure()
{
// ??? is it the best way to check if there was a problem with the channel?
if (((IChannel)_client).State != CommunicationState.Opened)
{
// ??? is it safe to call Abort? won't it throw?
((IChannel)_client).Abort();
}
// ??? and what about ChannelFactory?
// will it still be able to create channels or it also might be broken and must be thrown away?
// In that case, how do I clean up ChannelFactory correctly before creating a new one?
}
#region IDisposable
public void Dispose()
{
// ??? is it how to free the channel correctly?
// I've heard, broken channels might throw when closing
// ??? what if it is not opened yet?
// ??? what if it is in fault state?
try
{
((IChannel)_client).Close();
}
catch
{
((IChannel)_client).Abort();
}
((IDisposable)_client).Dispose();
_client = null;
_isDisposed = true;
}
#endregion
}
【问题讨论】:
-
我最终实现了几乎与上述类似的实现,而且它似乎工作正常。我在 RecoverFromChannelFailure 中添加了一些代码来处理损坏的工厂:
lock (_lock){ if (_factory.State != CommunicationState.Opened) {_factory.Abort();_factory = null;}};而且我还有一个 Initialize 方法,它检查工厂是否已经消失,然后创建一个新的。 -
关于身份验证,我最终得到了一个自定义
MessageInterceptorBehavior : IEndpointBehavior, IClientMessageInspector, IDispatchMessageInspector,它具有被 WCF 为服务器和客户端调用的 AfterReceiveRequest 方法。 -
感谢您的更新!处理破碎的工厂是一个我可以忘记的案例。顺便说一句,我在重用客户端通道时遇到了一些问题:在跟踪中看到频繁但看起来随机的 TCP 995 异常;这就是我问的原因。最后,重用工厂但每次都重新创建客户渠道为我解决了这个问题。由于下面的 TCP 连接是池化的,它似乎没有太大的成本,虽然我没有测量。
标签: wcf dispose channel channelfactory recover