我的 WCF 客户端库有以下计划架构:
我需要更多的控制权,而且我想把客户分开
当我的 WCF 服务更改时组装并避免重新生成
端点,因此每个 channel 都能够发送其
自己的身份验证 token
我已经阅读了许多关于各种不同位的文章,但我仍然对如何以正确的方式将它们组合在一起感到困惑。我有以下问题:
我猜,一行代码说一千多个单词,所以这是我的代码形式的想法。我已经用“???”标记了我上面的所有问题在代码中。
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
}
最佳答案
我想晚一点再好一点……而且看起来作者已经在工作了,这可能对 future 的 WCF 用户有所帮助。
1) ChannelFactory 安排包含 channel 所有行为的 channel 。通过 CreateChannel 方法创建 channel 会“激活” channel 。 channel 工厂可以被缓存。
2)你用绑定(bind)和行为来塑造 channel 工厂。此形状与创建此 channel 的每个人共享。正如您在评论中指出的那样,您可以附加消息检查器,但更常见的情况是使用 Header 将自定义状态信息发送到服务。您可以通过 OperationContext.Current 附加 header
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);
}
3)这是我处理客户端 channel 和工厂的一般方法(此方法是我的 ProxyBase 类的一部分)
public virtual void Dispose()
{
CloseChannel();
CloseFactory();
}
protected void CloseChannel()
{
if (((IChannel)_client).State == CommunicationState.Opened)
{
try
{
((IChannel)_client).Close();
}
catch (TimeoutException /* timeout */)
{
// Handle the timeout exception
((IChannel)innerChannel).Abort();
}
catch (CommunicationException /* communicationException */)
{
// Handle the communication exception
((IChannel)_client).Abort();
}
}
}
protected void CloseFactory()
{
if (Factory.State == CommunicationState.Opened)
{
try
{
Factory.Close();
}
catch (TimeoutException /* timeout */)
{
// Handle the timeout exception
Factory.Abort();
}
catch (CommunicationException /* communicationException */)
{
// Handle the communication exception
Factory.Abort();
}
}
}
4) WCF 将故障 channel 而不是工厂。您可以实现重新连接逻辑,但这需要您从某些自定义 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) 不要重复使用 channel 。打开,做某事,关闭是正常的使用模式。
6)创建通用代理基类并从中派生所有客户端。这可能会有所帮助,例如重新连接、使用调用前/调用后逻辑、使用工厂中的事件(例如故障、打开)
7)创建您自己的 CustomChannelFactory 这使您可以进一步控制工厂的行为方式,例如设置默认超时,强制执行各种绑定(bind)设置(MaxMessageSizes)等。
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;
}
}
关于WCF ChannelFactory 和 channel - 缓存、重用、关闭和恢复,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14547034/