私はブロック内で WCF サービス クライアントをインスタンス化することを好みます。using
これは、実装するリソースを使用する標準的な方法だからですIDisposable
。
using (var client = new SomeWCFServiceClient())
{
//Do something with the client
}
しかし、このMSDNの記事、WCF クライアントをusing
ブロックでラップすると、クライアントが障害状態 (タイムアウトや通信の問題など) のままになるエラーがマスクされる可能性があります。簡単に言うと、Dispose()
が呼び出されると、クライアントのClose()
メソッドが起動しますが、障害状態にあるためエラーがスローされます。元の例外は、2 番目の例外によってマスクされます。これは良くありません。
MSDN の記事で提案されている回避策は、using
ブロックの使用を完全に避け、代わりにクライアントをインスタンス化して次のように使用することです。
try
{
...
client.Close();
}
catch (CommunicationException e)
{
...
client.Abort();
}
catch (TimeoutException e)
{
...
client.Abort();
}
catch (Exception e)
{
...
client.Abort();
throw;
}
ブロックと比較するとusing
、これは見苦しいと思います。また、クライアントが必要になるたびに大量のコードを書く必要があります。
幸運にも、私は他の回避策をいくつか見つけました。たとえば、(現在は閉鎖されている) IServiceOriented ブログにある回避策です。まずは次のようにします。
public delegate void UseServiceDelegate<T>(T proxy);
public static class Service<T>
{
public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>("");
public static void Use(UseServiceDelegate<T> codeBlock)
{
IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
bool success = false;
try
{
codeBlock((T)proxy);
proxy.Close();
success = true;
}
finally
{
if (!success)
{
proxy.Abort();
}
}
}
}
これにより、次のことが可能になります。
Service<IOrderService>.Use(orderService =>
{
orderService.PlaceOrder(request);
});
それは悪くはないですが、ブロックほど表現力があり、理解しやすいとは思いませんusing
。
私が現在試している回避策は、私が最初に読んだブログ基本的に、クライアントのDispose()
メソッドを使用する場所でそれをオーバーライドします。次のようになります。
public partial class SomeWCFServiceClient : IDisposable
{
void IDisposable.Dispose()
{
if (this.State == CommunicationState.Faulted)
{
this.Abort();
}
else
{
this.Close();
}
}
}
using
これにより、障害状態の例外をマスクする危険なしに、再度ブロックを許可できるようです。
では、これらの回避策を使用する際に注意すべき他の落とし穴はありますか? 誰かもっと良い方法を思いついた人はいますか?
ベストアンサー1
実は、私はブログに書いた(見るルークの答え)、 私は思うこれ私のIDisposableラッパーよりも優れています。典型的なコード:
Service<IOrderService>.Use(orderService=>
{
orderService.PlaceOrder(request);
});
(コメントに従って編集)
void を返すためUse
、戻り値を処理する最も簡単な方法は、キャプチャされた変数を使用することです。
int newOrderId = 0; // need a value for definite assignment
Service<IOrderService>.Use(orderService=>
{
newOrderId = orderService.PlaceOrder(request);
});
Console.WriteLine(newOrderId); // should be updated