WCF ChannelFactory和通道-缓存、重用、关闭和恢复
我为我的WCF客户端库规划了以下体系结构:WCF ChannelFactory和通道-缓存、重用、关闭和恢复,wcf,dispose,channel,channelfactory,recover,Wcf,Dispose,Channel,Channelfactory,Recover,我为我的WCF客户端库规划了以下体系结构: 使用ChannelFactory而不是svcutil生成代理,因为 我需要更多的控制,而且我想让客户在一个单独的 汇编并避免在我的WCF服务更改时重新生成 需要将带有消息检查器的行为应用于我的WCF 端点,因此每个通道都能够发送其 自有认证令牌 我的客户端库将从MVC前端使用,因此我必须考虑可能的线程问题 我正在使用.NET4.5(也许它有一些帮助工具或新方法可以更好地实现WCF客户端?) 我已经读了很多关于各种独立位的文章,但我仍然不知道如何以正
- 使用ChannelFactory而不是svcutil生成代理,因为 我需要更多的控制,而且我想让客户在一个单独的 汇编并避免在我的WCF服务更改时重新生成
- 需要将带有消息检查器的行为应用于我的WCF 端点,因此每个通道都能够发送其 自有认证令牌
- 我的客户端库将从MVC前端使用,因此我必须考虑可能的线程问题
- 我正在使用.NET4.5(也许它有一些帮助工具或新方法可以更好地实现WCF客户端?)
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
}
公共类MyServiceClient:IDisposable
{
//通道工厂缓存
私人静态通道工厂(u工厂),;
私有静态对象_lock=新对象();
私有IMyService_client=null;
私有bool_isDisposed=false;
///
///为服务创建一个通道
///
公共MyServiceClient()
{
锁
{
如果(_factory==null)
{
//…在此处设置自定义绑定并获取一些配置值
var endpoint=新的端点地址(myServiceUrl);
_工厂=新通道工厂(绑定,端点);
//我是否为整个ChannelFactory添加身份验证行为
//或者我可以在创建单个频道时将其应用于它们?
}
}
_客户端=_factory.CreateChannel();
}
公共字符串MyMethod()
{
RequireClientInWorkingState();
尝试
{
返回_client.MyMethod();
}
抓住
{
RecoverFromChannelFailure();
投掷;
}
}
私有void RequireClientInWorkingState()
{
如果(_isDisposed)
抛出新的InvalidOperationException(“此客户端已被释放。创建一个新客户端”);
//?检查通信状态是否已打开和创建就足够了?
if(state!=CommunicationState.Created&&state!=CommunicationState.Opened)
抛出新的InvalidOperationException(“客户端通道未准备好工作。请创建一个新通道”);
}
private void RecoverFromChannelFailure()
{
//??这是检查通道是否存在问题的最佳方法吗?
if(((IChannel)\u client.State!=通信状态.Opened)
{
//?调用中止安全吗?它不会抛出吗?
((IChannel)u client.Abort();
}
//??那么ChannelFactory呢?
//它是否仍然能够创建频道,或者它也可能被破坏,必须扔掉?
//在这种情况下,如何在创建新的ChannelFactory之前正确清理ChannelFactory?
}
#可识别区域
公共空间处置()
{
//??如何正确地释放通道?
//我听说,关闭时坏掉的频道可能会抛出
//??如果它还没有打开怎么办?
//??如果它处于故障状态怎么办?
尝试
{
((IChannel)u client.Close();
}
抓住
{
((IChannel)u client.Abort();
}
((IDisposable)u client.Dispose();
_client=null;
_isDisposed=true;
}
#端区
}
我想晚一点总比不。。。看起来作者已经让它工作了,这可能会帮助未来的WCF用户
1) ChannelFactory排列通道,其中包括通道的所有行为。通过CreateChannel方法创建通道“激活”通道。可以缓存通道工厂
2) 您可以使用绑定和行为来塑造通道工厂。此形状与创建此频道的所有人共享。正如您在评论中所指出的,您可以附加消息检查器,但更常见的情况是使用头向服务发送自定义状态信息。您可以通过OperationContext.Current附加标题
using (var op = new OperationContextScope((IContextChannel)proxy))
{
var header = new MessageHeader<string>("Some State");
var hout = header.GetUntypedHeader("message", "urn:someNamespace");
OperationContext.Current.OutgoingMessageHeaders.Add(hout);
}
4) WCF将故障通道,而不是工厂。您可以实现重新连接逻辑,但这需要您从一些自定义ProxyBase(例如
protected I Channel
{
get
{
lock (_channelLock)
{
if (! object.Equals(innerChannel, default(I)))
{
ICommunicationObject channelObject = innerChannel as ICommunicationObject;
if ((channelObject.State == CommunicationState.Faulted) || (channelObject.State == CommunicationState.Closed))
{
// Channel is faulted or closing for some reason, attempt to recreate channel
innerChannel = default(I);
}
}
if (object.Equals(innerChannel, default(I)))
{
Debug.Assert(Factory != null);
innerChannel = Factory.CreateChannel();
((ICommunicationObject)innerChannel).Faulted += new EventHandler(Channel_Faulted);
}
}
return innerChannel;
}
}
5) 不要重复使用频道。打开、做某事、关闭是正常的使用模式
6) 创建公共代理基类并从中派生所有客户端。这会很有帮助,比如使用pre-invoke/po重新连接
protected I Channel
{
get
{
lock (_channelLock)
{
if (! object.Equals(innerChannel, default(I)))
{
ICommunicationObject channelObject = innerChannel as ICommunicationObject;
if ((channelObject.State == CommunicationState.Faulted) || (channelObject.State == CommunicationState.Closed))
{
// Channel is faulted or closing for some reason, attempt to recreate channel
innerChannel = default(I);
}
}
if (object.Equals(innerChannel, default(I)))
{
Debug.Assert(Factory != null);
innerChannel = Factory.CreateChannel();
((ICommunicationObject)innerChannel).Faulted += new EventHandler(Channel_Faulted);
}
}
return innerChannel;
}
}
public static void SetTimeouts(Binding binding, TimeSpan? timeout = null, TimeSpan? debugTimeout = null)
{
if (timeout == null)
{
timeout = new TimeSpan(0, 0, 1, 0);
}
if (debugTimeout == null)
{
debugTimeout = new TimeSpan(0, 0, 10, 0);
}
if (Debugger.IsAttached)
{
binding.ReceiveTimeout = debugTimeout.Value;
binding.SendTimeout = debugTimeout.Value;
}
else
{
binding.ReceiveTimeout = timeout.Value;
binding.SendTimeout = timeout.Value;
}
}