私の WCF クライアント ライブラリには、次のアーキテクチャが計画されています。
- より多くの制御が必要であり、クライアントを別のアセンブリに保持し、WCF サービスが変更されたときに再生成を回避したいため、svcutil で生成されたプロキシの代わりに ChannelFactory を使用する
- メッセージ インスペクターを使用して動作を WCF エンドポイントに適用する必要があるため、各チャネルは独自の認証トークンを送信できます。
- 私のクライアント ライブラリは MVC フロントエンドから使用されるため、考えられるスレッドの問題について考える必要があります。
- 私は.NET 4.5を使用しています(WCFクライアントをより良い方法で実装するためのヘルパーまたは新しいアプローチがあるかもしれませんか?)
さまざまな個別のビットに関する多くの記事を読みましたが、すべてを正しい方法でまとめる方法についてはまだ混乱しています。次の質問があります。
- 私が理解しているように、 ChannelFactory を静的変数にキャッシュしてから、そこからチャネルを取得することをお勧めしますよね?
- エンドポイントの動作は ChannelFactory 全体に固有のものですか、それとも各チャネルに個別に認証動作を適用できますか? 動作がファクトリ全体に固有のものである場合、これはエンドポイントの動作オブジェクトに状態情報を保持できないことを意味します。これは、同じ認証トークンがすべてのチャネルで再利用されるためですが、明らかに各チャネルには独自の認証トークンが必要です。現在の使用者。つまり、エンドポイントの動作内でトークンを計算する必要があります (HttpContext にトークンを保持でき、メッセージ インスペクターの動作はそれを発信メッセージに追加するだけです)。
- 私のクライアント クラスは使い捨てです (IDispose を実装します)。可能な状態 (開かれていない、開かれていない、失敗した...) にある可能性があることを知って、チャネルを正しく破棄するにはどうすればよいですか? 処分するだけですか?中絶してから処分しますか?閉じて (ただし、まだまったく開いていない可能性があります)、破棄しますか?
- チャンネルで作業中にエラーが発生した場合はどうすればよいですか? 壊れているのはチャネルだけですか、それとも ChannelFactory 全体が壊れていますか?
1 行のコードは 1,000 語以上を話すので、ここに私の考えをコード形式で示します。上記のすべての質問に「???」を付けました。コードで。
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
}