10

例外をログに記録し、それらを FaultExceptions としてスローする WCF サービスがあります。

各サービスメソッドなどで多くの繰り返しを行っています。

try { 
   // do some work

}
catch(Exception ex)
{
  Logger.log(ex);

  // actually will be Fault Exception but you get the idea.
  throw ex;
}

各サービス全体で try/catch をカット アンド ペーストしているため、これを行うためのよりエレガントな方法を探しています。

これをよりエレガントにするために使用できるデザインパターン/C#トリックはありますか?

4

9 に答える 9

9

あなたはAOPについて話している-Aspect Oriented Programming

「作業」をラムダとして渡す方法は次のとおりです。

public partial static class Aspect
{
  public static T HandleFaultException<T>( Func<T> fn )
  {
    try
    { 
      return fn();
    }
    catch( FaultException ex )
    {
      Logger.log(ex);
      throw;
    }
  }
}

それを使用するには:

return Aspect.HandleFaultException( () =>
  {
    // call WCF
  }
);

同じ目標を達成する方法は他にもあり、商用製品もありますが、私はこの方法が最も明確で柔軟であると考えています。

たとえば、クライアントを作成および破棄するアスペクトを作成できます。

public partial static class Aspect
{
  public static T CallClient<T>( Func<Client, T> fn )
  {
    using ( var client = ... create client ... )
    {
      return fn( client );
    }
  }
}

など:

return Aspect.CallClient( client =>
  {
    return client.Method( ... );
  }
);

次に、通常適用するすべてのアスペクトをラップして、1 つのマスター アスペクトを作成します。

于 2013-03-04T14:15:32.280 に答える
5

WCFサービスの1つでも同様の問題が発生します。これは、ヘルパーデリゲートを使用して解決しました。

public static void ErrorHandlingWrapper(Action DoWork)
{
    try { 
        DoWork();
    }
    catch(Exception ex)
    {
        Logger.log(ex);

        // actually will be Fault Exception but you get the idea.
        throw;
    }
}

使用法:

public void MyMethod1()
{
    ErrorHandlingWrapper(() => {
        // do work
    });
}

public void MyMethod2()
{
    ErrorHandlingWrapper(() => {
        // do work
    });
}

それでもラッパーを繰り返す必要がありますが、それはコードがはるかに少なく、ロジックをtry..catch1か所で変更できます。

于 2013-03-04T14:09:37.203 に答える
3

WCF 固有の場合は、独自の ErrorHandler を追加することを検討してください。これにより、メソッドが例外をスローするたびに独自のコードを「注入」して実行することができます。

次のように設定できます。

serviceHost.Description.Behaviors.Add(new ErrorHandlerBehavior()); //Add your own ErrorHandlerBehaviour

public class ErrorHandlerBehavior : IErrorHandler, IServiceBehavior
{
    private static readonly Logger log = LogManager.GetCurrentClassLogger();

    public bool HandleError(Exception error)
    {
        if (error is CommunicationException)
        {
            log.Info("Wcf has encountered communication exception.");
        }
        else
        {
            // Log
        }

        return true;
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
        //Here you can convert any exception to FaultException like this:
        if (error is FaultException)
            return;

        var faultExc = new FaultException(error.Message);
        var faultMessage = faultExc.CreateMessageFault();

        fault = Message.CreateMessage(version, faultMessage, faultExc.Action);
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, 
        BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcherBase channelDispatcher in serviceHostBase.ChannelDispatchers)
        {
            var channelDisp = channelDispatcher as ChannelDispatcher;

            if (channelDisp != null)
                channelDisp.ErrorHandlers.Add(this);
        }
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }
}

これにより、ビジネス レイヤーを変更したり、try/catch コードを適用してそこで変換したりすることなく、後で WCF によって適切に処理されるすべての種類の例外をスローし、Fault Exceptions に変換することができます。

于 2013-03-04T14:08:02.243 に答える
1

AspectF (私のものではありません) を試してみてください: http://www.codeproject.com/Articles/42474/AspectF-Fluent-Way-to-Add-Aspects-for-Cleaner-Main。これは Omar Al-Zabir によるものです... Dropthings フレームワークの作成者など。これがお役に立てば幸いです。

于 2013-03-04T14:12:16.493 に答える
1

テンプレート メソッド パターンはまさにこれを行っており、C# のデリゲート (またはラムダ) を使用して継承なしで簡単に実装できます。

于 2013-03-04T14:06:37.240 に答える
0

一般的に言えば、冗長な仕事をするいくつかの例外ハンドラーを書くことができます:

public abstract class ExceptionHandler
{
    /// Returns true if the exception is handled; otherwise returns false.
    public abstract bool Handle(Exception ex);

    protected void Log(Exception ex)
    {
        // Log exception here
    }
}

public class FileExceptionHandler : ExceptionHandler
{
    public override bool Handle(Exception ex)
    {
        this.Log(ex);

        // Tries to handle exceptions gracefully
        if (ex is UnauthorizedAccessException)
        {
            // Add some logic here (for example encapsulate the exception)
            // ...
            return true;
        }
        else if (ex is IOException)
        {
            // Another logic here
            // ...
            return true;
        }

        // Did not handled the exception...
        return false;
    }
}

public class Program
{
    private static void Main(string[] args)
    {
        try
        {
            // File manipulation
            throw new IOException();
        }
        catch (Exception ex)
        {
            if (!new FileExceptionHandler().Handle(ex))
            {
                // Exception not handled, so throw exception
                throw;
            }
        }

        Console.WriteLine("end");
    }
}
于 2013-03-04T14:17:36.130 に答える
0

単純にスローして、呼び出し元のスタックの上に例外を伝播する場合は、すべての深いレベルで例外を処理することを避け、最上位の (可能な) レベルでキャッチしてログに記録するだけです。Exceptionオブジェクトにはスタック トレースも含まれていることに注意してください。

ちなみに、すべてのレベルでキャッチが必要な場合は、 use を覚えておいてくださいthrow。そのため、コール スタックは影響を受けず、代わりに使用方法で影響を受けます。

于 2013-03-04T14:07:19.263 に答える
0

Application_Errorファイル内のイベントをサブスクライブすることもできglobal.asaxます。

デスクトップ アプリケーションの場合、 にUnhandledExceptionイベントがありAppDomainます。

于 2013-03-04T14:07:41.527 に答える
0

あなたの質問が、現在のパターンをより速く作業できるようにする方法に関するものである場合は、Snippet

<CodeSnippets
    xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>
                trylog
            </Title>
            <Shortcut>
                trylog
            </Shortcut>
        </Header>
        <Snippet>
            <Code Language="CSharp">
                <![CDATA[try { 
   // do some work

}
catch(Exception ex)
{
  Logger.log(ex);

  // actually will be Fault Exception but you get the idea.
  throw ex;
}]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>
于 2013-03-04T14:32:36.893 に答える