1

別のボックスでSQLサーバーに接続するEntityFrameworkアプリケーションがあります。プログラムフローは、次の2つの状態に分類できます。

  1. SimpleInjectorDIフレームワークを使用してコンポジションルートとレジスタタイプを初期化します
  2. Initアプリケーション(Entity Frameworkを使用してSQLデータベースで読み取りと書き込みを行います)
  3. タイマーに基づいて、タスク関数は実行予定のコマンドのインスタンスを取得しますICommandHandler<CommandType>(コマンドタイプは異なります)
  4. Handle(commandType)このインスタンスを呼び出してコマンドを実行します
  5. 手順3に戻ります

SQLサーバーへの接続が失われた場合にプログラムがクラッシュしないように保護する必要があります。現在、アプリケーションがSQL接続を失うと、未処理の例外がスローされEntityException - The underlying provider failed on Openます。

サーバーがオンラインに戻ると、プログラムはリセットして操作を再開できるはずです。

この質問には、アプリケーションのコアであるSimple Injectorの使用が含まれます。初期化されていない状態と実行中の状態の間の状態遷移を記述することについていくつかのアイデアがありますが、最初に、 Simple Injectorの機能-特に私はデコレータに焦点を当てていますが、これが正しいかどうかはわかりません。

他の推奨アーキテクチャを公開し、エラーがより高いレベルでキャッチされて状態の変化が発生する場所からこれがどのように見えるかを確認します。

以下のコード構造

私はコマンド/ハンドラーアプローチを使用しています:

public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

アプリケーションの起動時に、実装するすべてのタイプICommandHandler<T>が登録されます。

public static void Bootstrap(Container container)
{
    container.RegisterManyForOpenGeneric(
        typeof(ICommandHandler<>),
        System.AppDomain.CurrentDomain.GetAssemblies());

    // bootstrap container
}

私のコマンドハンドラーの実装は次のとおりです。

public class AddBusinessUnitCommand
{
    public string Name { get; set; }
    public float TimeZone { get; set; }
}

public class BusinessUnitCommandHandlers
    : ICommandHandler<AddBusinessUnitCommand>
{
    private IBusinessUnitService businessUnitService;

    public BusinessUnitCommandHandlers(
        IBusinessUnitService businessUnitService)
    {
        this.businessUnitService = businessUnitService;
    }

    public void Handle(AddBusinessUnitCommand command)
    {
        // do something
    }
}

次に、Simple Injectorを使用して、タイプのインスタンスを取得できます。たとえば、ICommandHandler<AddBusinessUnitCommand>インスタンス化されたBusinessUnitCommandHandlersオブジェクトが返されHandle()、コマンドを実行できるようになります。

Handle()Simple Injectorは、プロシージャ呼び出しをラップするために使用できるデコレータを使用できることを確認しました。

public class TransactionCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    private readonly ICommandHandler<TCommand> handlerToCall;
    private readonly IUnitOfWork unitOfWork;

    public TransactionCommandHandlerDecorator(
        IUnitOfWork unitOfWork, 
        ICommandHandler<TCommand> decorated)
    {
        this.handlerToCall = decorated;
        this.unitOfWork = unitOfWork;
    }

    public void Handle(TCommand command)
    {
         this.handlerToCall.Handle(command);
         unitOfWork.Save();
    }
}
4

1 に答える 1

1

SQLサーバーへの接続が失われた場合にプログラムがクラッシュしないように保護する必要があります。

... Simple Injectorの機能を使用してエラーを適切にキャッチする必要がある場所-具体的には、デコレータに焦点を当てていますが、これが正しいかどうかはわかりません。

ICommandHandler<T>一般的なデコレータで実装をデコレートする3つの重要な理由があります。まず第一に、デコレータは一度作成され、多くのICommandHandler<T>実装をラップするため、アプリケーションに重複するコードが含まれるのを防ぎます。次に、コマンドハンドラーの動作を変更し、これらのコマンドハンドラーのコンシューマーを変更することなく、横断的関心事を追加できます。最後に、コードの再利用とコンシューマーの無知が問題にならない場合でも、追加する動作が論理的にコマンドハンドラーの一部である場合は、デコレーターが役立ちます。たとえば、実行されたコマンドを検証します。これは、消費者がおそらく気にするべきではないことであり、への呼び出し中に実行したいことですHandle

「コマンドプロセッサ」(タイマー)は単一のコードであり、アプリケーションのインフラストラクチャの一部(おそらくコンポジションルートの一部)であると思います。これは単一のコードであるため、コードの重複を防ぐためにデコレータを使用する理由はありません。デコレータはおそらくそれほど多くを獲得しないため、このクラッシュ保護を追加する必要はありません。

特に、このコードはコマンドの再スケジュールなどを実行する必要があるため、クラッシュ保護を実行するのはこのインフラストラクチャコードの責任である可能性があると主張することもできます(失敗の種類)、または「失敗したコマンドキュー」のようなものに配置します(手動で再実行できるようにするため)。おそらく、サーキットブレーカパターンを使用することで設計にメリットがもたらされる可能性があります。

于 2012-09-22T19:05:25.970 に答える