このロギング分野横断的な問題をアプリケーションに追加する方法を改善できる可能性があるように私には思えます。
ここでの主な問題は、ソリューションによってSomeClass.SomeMethod
(または呼び出されたメソッド) に変更を加えることはできませんが、使用するコードに変更を加える必要があることです。つまり、既存のコードを変更せずにこの種の変更を行うことが可能でなければならないというオープン/クローズの原則を破っています。
大げさだと思うかもしれませんがHandleServerError
、アプリケーションにはすでに 100 を超える呼び出しがあり、呼び出しの数は増える一方です。そして、これらの「関数型デコレーター」をさらにシステムに追加する予定です。承認チェック、メソッド引数の検証、インストルメンテーション、または監査追跡を行うことについて考えたことはありますか? そして、あなたはそれをするnew Func<T>(() => someCall).HandleServerError()
のが面倒だと認めなければなりませんね。
システムに適切な抽象化を導入することで、実際の質問の問題を含め、これらすべての問題を解決できます。
最初のステップは、指定されたメソッド引数をParameter Objectに昇格させることです:
public SomeMethodParameters
{
public int Id { get; set; }
public string Name { get; set; }
public Color Color { get; set; }
public decimal Quantity { get; set; }
public decimal Result { get; set; }
}
個々の引数をすべてメソッドに渡す代わりに、それらをすべて 1 つのオブジェクトとしてまとめて渡すことができます。それは何の役に立ちますか?読む。
SomeClass.SomeMethod
2 番目のステップは、背後にある (または実際には任意のメソッド)の実際のロジックを非表示にする汎用インターフェイスを導入することです。
public interface IMethodHandler<TParameter>
{
void Handle(TParameter parameter);
}
IMethodHandler<TParameter>
システム内の (ビジネス) 操作ごとに、実装を作成できます。あなたの場合、次のSomeClass.SomeMethod
ように、への呼び出しをラップする実装を簡単に作成できます。
public class SomeMethodHandler
: IMethodHandler<SomeMethodParameters>
{
void Handle(SomeMethodParameters parameter)
{
parameter.Result = SomeClass.SomeMethod(
parameter.id,
parameter.Name,
parameter.Color,
parameter.Quantity);
}
}
このようなことを行うのは少しばかげているように見えるかもしれませんが、この設計を迅速に実装し、 static のロジックをSomeClass.SomeMethod
内に移動することができますSomeMethodHandler
。
3番目のステップは、消費者IMethodHandler<SomeMethodParameters>
がシステム内の静的メソッドに依存するのではなく、インターフェースに依存できるようにすることです(あなたの場合は再びSomeClass.SomeMethod
.)。このような抽象化に依存することの利点を少し考えてみてください。
これによる興味深い結果の 1 つは、コンシューマーの単体テストがはるかに簡単になることです。しかし、単体テストには興味がないかもしれません。しかし、あなたは疎結合に興味があります。消費者が実際の実装 (特に静的メソッド) ではなく、そのような抽象化に依存している場合、ロギングなどの分野横断的な懸念事項を追加するなど、あらゆる種類のクレイジーなことを行うことができます。IMethodHandler<T>
これを行う良い方法は、実装をデコレータでラップすることです。ユースケースのデコレータは次のとおりです。
public class LoggingMethodHandlerDecorator<T>
: IMethodHandler<T>
{
private readonly IMethodHandler<T> handler;
public LoggingMethodHandlerDecorator(
IMethodHandler<T> handler)
{
this.handler = handler;
}
public void Handle(T parameters)
{
try
{
this.handler.Handle(parameters);
}
catch (Exception ex)
{
//******************************
//Code for logging will go here.
//******************************
ErrorHandlers.ThrowServerErrorException(ex);
throw;
}
}
}
Handle
このデコレーターのメソッドに、元のメソッドのコードがどのように含まれているかがわかりますHandleServerError<T>
か? HandleServerError
実際には、元のメソッドの動作を新しい動作で「装飾」(または「拡張」) したため、既に行っていたこととそれほど違いはありません。しかし、現在メソッド呼び出しを使用する代わりに、オブジェクトを使用しています。
このすべての良い点は、この単一のジェネリックをすべての単一の実装LoggingMethodHandlerDecorator<T>
にラップでき、すべてのコンシューマーが使用できることです。IMethodHandler<T>
このようにして、消費者とメソッドの両方がこれを知ることなく、ロギングなどの横断的な懸念を追加できます。これがオープン/クローズの原則です。
しかし、これには他にも本当に素晴らしいことがあります。最初の質問は、メソッド名とパラメーターに関する情報を取得する方法に関するものでした。Func
デリゲート内にラップされたカスタム メソッドを呼び出す代わりに、すべての引数をオブジェクト内にラップしたため、このすべての情報を簡単に利用できるようになりました。catch
次のように句を実装できます。
string messageInfo = string.Format("<{0}>{1}</{0}>",
parameters.GetType().Name, string.Join("",
from property in parameters.GetType().GetProperties()
where property.CanRead
select string.Format("<{0}>{1}</{0}>",
property.Name, property.GetValue(parameters, null)));
これにより、TParameter オブジェクトの名前とその値が XML 形式にシリアル化されます。もちろん、.NET を使用XmlSerializer
してオブジェクトを XML にシリアル化するか、必要なその他のシリアル化を使用することもできます。メタデータで利用可能な場合はすべての情報。これは非常に優れています。パラメータ オブジェクトに適切で一意の名前を付けると、ログ ファイルですぐに識別することができます。そして、実際のパラメーターとおそらくいくつかのコンテキスト情報 (日時、現在のユーザーなど) とともに、バグを修正するために必要なすべての情報が得られます。
LoggingMethodHandlerDecorator<T>
これと元の の間に 1 つの違いがありHandleServerError<T>
、それが最後のthrow
ステートメントです。あなたの実装ON ERROR RESUME NEXT
は、最善の方法ではないかもしれない何らかの種類を実装しています。メソッドが失敗したときに続行する (そしてデフォルト値を返す) ことは実際に安全ですか? 私の経験では、通常はそうではなく、この時点で続行すると、消費するクラスを作成する開発者は、すべてが期待どおりに機能すると考えたり、アプリケーションのユーザーに、すべてが期待どおりに機能したと考えさせたりする可能性があります (彼の変更たとえば、保存されていましたが、実際には保存されていませんでした)。通常、これについてできることはあまりなく、すべてをcatch
この情報をログに記録したいことは想像できますが、ステートメントは悪化するだけです。「アプリケーションは常に動作する必要がある」または「エラー ページを表示したくない」などのユーザー要件に惑わされないでください。すべてのエラーを抑制してこれらの要件を実装しても、根本的な原因を解決することはできません。それでも、本当にキャッチ アンド コンティニュが必要な場合は、`throw
ステートメントを削除するだけで、元の動作に戻ります。
システムを設計するこの方法について詳しく知りたい場合は、ここから始めてください。