我为我的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
}
lock (_lock){ if (_factory.State != CommunicationState.Opened) {_factory.Abort();_factory = null;}}
;并且我还有一个Initialize方法,它检查工厂是否已经消失,然后创建一个新的工厂。 - JustAMartinMessageInterceptorBehavior:IEndpointBehavior,IClientMessageInspector,IDispatchMessageInspector
,该方法具有AfterReceiveRequest方法,WCF会在服务器和客户端端都调用它。 - JustAMartin