10

そのコンストラクターが依存関係を取り、それが IoC コンテナー (この場合は AutoFac) による依存関係のインスタンス化である場合に、WCF サービスのコンストラクターによってスローされた例外を処理する方法はありますか?

次のコンストラクターを持つ WCF サービスを検討してください。

public InformationService(IInformationRetriever informationRetriever)
{
    _informationRetriever = informationRetriever;
}
//... the service later makes use of the injected InformationRetriever

このサービスは、AutoFac WcfIntegration とAutofacWebServiceHostFactory(たまたま RESTful サービスである) を使用します。

依存関係は、サービスの global.asax.cs に登録されます。つまり、次のようになります。

builder.RegisterType<InformationRetriever>()
                .As<IInformationRetriever>()

これで、InformationRetriever実装はコンストラクターでいくつかのチェックを実行して、ジョブを実行できるようにすべてが整っていることを確認します。このフェーズで問題が検出されると、例外がスローされます。

ただし、サービスの呼び出し元が AutoFac 例外を受け取ることは望ましくありません。

An exception was thrown while invoking the constructor ... on type InformationRetriever

効果的に私はテストしようとしています:

InformationServiceが実行されている場合

GetSomeInformation () メソッドを呼び出すと

また、InformationRetriever をインスタンス化できません

次に、わかりやすいエラーメッセージを返したい

実際の例外をログに記録します

これは私のデザインの問題ですか、それともこの問題を克服または防止するための既知のパターンはありますか?

私は探し回りましたが、この種の問題に関する情報は見つかりませんでした。

4

2 に答える 2

11

DI スタイルで記述されたオブジェクトは、通常、構成と実行という 2 つの別個のフェーズを通過します。コンポジション フェーズでは、依存関係を結び付けたり、引数の例外をスローしたりします。通常、このフェーズでは意味のある動作を行わないようにする必要があります。これにより、システムの構成でエラーが表面化する可能性があります。2 番目のフェーズである実行では、最初のフェーズの出力 (依存関係) を使用して作業を行います。

これら 2 つのフェーズを分離することで、多くのあいまいさと複雑さが解消されます。例として、芝刈り機にガスを入れている間は芝生を刈ろうとしません。これにより、両方のアクティビティがより複雑になります (そして危険です!)

この場合、InformationRetrieverコンストラクターで意味のある作業を実行することにより、構成フェーズと実行フェーズを混同しています。この混合は、まさに回避しようとしている問題を引き起こしています: 意味のあるビジネス例外が構成例外にラップされています。最上位の呼び出し元は Autofac であり、実際にInformationRetriever作業を要求しているコンポーネントではないため、例外の処理方法も不明です。

を呼び出すときに検証を行うよう努めることをお勧めしInformationRetrieverます。これにより、Autofac 例外が削除され、トリックInformationServiceなしで例外的な状況を処理できるようになります。

InformationRetrieverこのアプローチの潜在的な欠点の 1 つは、コンストラクターで 1 回ではなく、 の呼び出しごとに検証が行われることです。2 つの選択肢があります: 1) 作業が有効であることを完全に確認するために、毎回実行するか、2) チェックを実行したかどうかを追跡し、まだ実行していない場合にのみ実行します。

#2 を選択した場合はInformationRetriever、デコレーターを使用して同じインターフェースの検証バージョンでラップすることにより、クリーンに保つことができます。

public class ValidatingInformationRetriever : IInformationRetriever
{
    private readonly IInformationRetriever _baseRetriever;
    private bool _validated;

    public ValidatingInformationRetriever(IInformationRetriever baseRetriever)
    {
        _baseRetriever = baseRetriever;
    }

    public void Foo()
    {
        if(!_validated)
        {
            Validate();

            _validated = true;
        }

        _baseRetriever.Foo();
    }

    private void Validate()
    {
        // ...
    }
}

次のように、 Autofac のデコレータ サポートを使用して登録できます。

builder
    .RegisterType<InformationRetriever>()
    .Named<IInformationRetriever>("base");

builder.RegisterDecorator<IInformationRetriever>(
    (c, inner) => new ValidatingInformationRetriever(inner),
    fromKey: "base");
于 2012-07-16T15:13:56.230 に答える
9

私は、コンストラクターが不適切な引数以外の理由で例外をスローするのが好きではありません。私はおそらく自分のタイプを別の方法でモデル化するでしょう。しかし、ここにいくつかのアイデアがあります。最初は、次のようなことを考えていました。

builder
    .Register(c => {
        try
        {
            return new InformationRetriever();
        }
        catch (Exception)
        {
            return new FailoverInformationRetreiver();
        }})
    .As<IInformationRetriever>();

...FailoverInformationRetreiverメンバーアクセスで例外をスローします。別のアイデアは、次のようにすることです。

public InformationService(Lazy<IInformationRetriever> informationRetriever)
{
    _informationRetriever = informationRetriever;
}

およびtry/catch内部の使用法についてInformationServiceInformationRetrieverアプリの起動時にの可用性がわかっている場合に使用できる別のオプション:

// inside your container builder:
if (InformationRetreiver.IsAvailable())
    builder.RegisterType<InformationRetriever>()
           .As<IInformationRetriever>()

// inside InformationService, make the dependency optional
public InformationService(IInformationRetriever informationRetriever = null)
{
    _informationRetriever = informationRetriever;
}

それらのアイデアは役に立ちますか?

于 2012-07-14T19:25:38.230 に答える