2011-04-11 11 views
11

かなり曖昧なタイトルの謝罪ですが、私が達成しようとしていることはおそらくコードでよりよく述べられています。C#:メソッド呼出しを包む優雅な方法

私はWCFクライアントを持っています。私がメソッドを呼び出すときには、エラー処理コードで各呼び出しをラップしたいと思います。だから、代わりに直接メソッドを公開するのは、私はクライアントクラスに、次のヘルパー関数を作成しました:pingに

service.HandleServiceCall(channel => channel.Ping("Hello")); 

そしてコール:

public T HandleServiceCall<T>(Func<IApplicationService, T> serviceMethod) 
    { 
     try 
     { 
      return serviceMethod(decorator); 
     } 
     [...] 
    } 

そして、クライアントコードはこのようにそれを使用していますエラーを処理しようとするロジックに素早くラップされます。

これは、サービスで実際に呼び出されているメソッドを知る必要があることを除いて、素晴らしいことです。最初は、表現木を使用してFunc<IApplicationService, T>を検査するだけで、あまり遠くまでは行かないことを期待していました。

は最後に、私はDecoratorパターンに落ち着い:

public T HandleServiceCall<T>(Func<IApplicationService, T> serviceMethod) 
    { 
     var decorator = new ServiceCallDecorator(client.ServiceChannel); 
     try 
     { 
      return serviceMethod(decorator); 
     } 
     [...] 
     finally 
     { 
      if (decorator.PingWasCalled) 
      { 
       Console.Writeline("I know that Ping was called") 
      } 
     } 
    } 

そして、デコレータ自体:

private class ServiceCallDecorator : IApplicationService 
    { 
     private readonly IApplicationService service; 

     public ServiceCallDecorator(IApplicationService service) 
     { 
      this.service = service; 
      this.PingWasCalled = new Nullable<bool>(); 
     } 

     public bool? PingWasCalled 
     { 
      get; 
      private set; 
     } 

     public ServiceResponse<bool> Ping(string message) 
     { 
      PingWasCalled = true; 
      return service.Ping(message); 
     } 
    } 

それは本当に不格好とコードのかなり多くのです。 これを行うもっとエレガントな方法はありますか?

+0

どこでデコレータを作成しますか? – smartcaveman

+3

エクスプレッションツリーは移動する方法です。コードを表示して問題の内容を教えてください。 –

+0

PostSharpの仕事のように聞こえます。 – geofftnz

答えて

2

public T HandleServiceCall<T>(Expression<Func<IApplicationService, T>> serviceMethod)  
{   
    try   
    {   
     var func = serviceMethod.Compile(); 
     string body = serviceMethod.Body.ToString(); 
     return func(new ConcreteAppService()); 
    }   
    catch(Exception ex) 
    { 
     ...  
       } 
} 
+1

これは、@ Enricoのメソッド呼び出しを検査する方法の説明と組み合わせてうまくいきます。それはまた、クライアントが単一のメソッド呼び出し以上に入るのを防ぐので、偶然に別の問題を解決します。これは素晴らしい追加です。 – djskinner

+0

私の最終的な解決策は@Richard Friendと@Enrico Campidoglioの回答の組み合わせです。ここでEnricoのコードを使用して呼び出されているメソッドを識別しています。 – djskinner

2

アスペクト指向のアプローチを検討しましたか?あなたが必要とするもののように聞こえます。

ラッピング例外やその他の「メタメソッド」機能は、あなたのserviceMethodsが行うこととは異なる側面として記述することができます。

AOPのいくつかの一般的な情報:AOP in wikipedia

とコンテナとの潜在的な解決策:AOP with Windsor Castle

+0

私は同意する、これはうまく収まる。私には少し残忍さが感じられます。よりシンプルでネイティブな方法がありますか? – djskinner

+0

より軽量なバージョンが(上記の)PostSharpになる可能性があります。私はそれを使用していない。 – grzeg

1

はここ式ツリーを使用して簡単な例です:

public T HandleServiceCall<T>(Expression<Func<T>> serviceMethod) 
{ 
    try 
    { 
     return serviceMethod(); 
    } 
    finally 
    { 
     var serviceMethodInfo = ((MethodCallExpression)serviceMethod.Body).Method; 
     Console.WriteLine("The '{0}' service method was called", serviceMethodInfo.Name); 
    } 
} 

(注)この例でいますserviceMethodの式には常にメソッド呼び出しが含まれているものとします。

関連リソース:あなたは身体を検査し、その後、式を使用することができ

+0

メソッド呼び出しは1つだけですか? – djskinner

+0

これをテストしようとしましたが、FuncにBodyの定義が含まれていません。 – djskinner

+1

'HandleServiceCall 'メソッドの引数の型を '式>' –

0

よう

何かはい、私はあなたのコードは煮ていると信じています。

プロキシの一般的な安全処分のためのコードをラップするという点では、hereは素晴らしい実装です。それは簡単です。使用: - 単にEnvironment.StackTraceを使用しているため

using (var client = new Proxy().Wrap()) { 
client.BaseObject.SomeMethod(); 
} 

今、あなたはまた、メソッド名にアクセスする必要があります。あなたは、Marc GravellのWrapのスタックを上に追加する必要があります。

+0

このアプローチは、自分のコードを設計したやり方とわずかに異なります。もし私がいくつかの重いリファクタリングをしたら、私はこれを間違いなく考えます。 – djskinner

関連する問題