1. ホーム
  2. c#

[解決済み】WCFクライアント `using` ブロックの問題に対する最良の回避策は何ですか?

2022-03-24 09:43:54

質問

WCFサービスクライアントのインスタンスを using を実装したリソースを利用するための標準的な方法だからです。 IDisposable :

using (var client = new SomeWCFServiceClient()) 
{
    //Do something with the client 
}

でも述べたように このMSDN記事 でWCFクライアントをラップすることです。 using ブロックは、クライアントを障害状態 (タイムアウトや通信の問題など) にするエラーを隠蔽することができます。簡単に説明すると Dispose() が呼び出されると、クライアントの Close() メソッドが起動しますが、faulted 状態であるため、エラーを投げます。このとき、元の例外は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 ブロックを使用します。

私が現在試している回避策は、最初に読んだのは blog.davidbarret.net . 基本的には、クライアントの Dispose() メソッドを使用します。のようなものです。

public partial class SomeWCFServiceClient : IDisposable
{
    void IDisposable.Dispose() 
    {
        if (this.State == CommunicationState.Faulted) 
        {
            this.Abort();
        } 
        else 
        {
            this.Close();
        }
    }
}

これによって using を再びブロックすることで、faulted stateの例外を隠してしまう危険性がありません。

では、これらの回避策を使って、他に気をつけなければならないことはあるのでしょうか?どなたか、もっと良い方法を思いついた方はいらっしゃいますか?

解決方法は?

実は、私は ブログ 参照 ルークからの回答 ) と思います。 これ は、私の IDisposable ラッパーよりも優れています。典型的なコードです。

Service<IOrderService>.Use(orderService=>
{
  orderService.PlaceOrder(request);
}); 


(コメントごとに編集)

から Use は void を返すので、戻り値を処理する最も簡単な方法は、キャプチャした変数を使うことです。

int newOrderId = 0; // need a value for definite assignment
Service<IOrderService>.Use(orderService=>
  {
    newOrderId = orderService.PlaceOrder(request);
  });
Console.WriteLine(newOrderId); // should be updated