3

コマンド ハンドラーの装飾に関するこの記事を読んだ後、今日は興味深い話題に乗り出しました。SimpleInjectorの代わりに Unity を使用してパターンを実装できるかどうかを確認したかったのですが、これまでのところ非常に難しいことがわかっています。

最初にやらなければならなかったことは、 UnityAutoRegistrationをインストールして、開いている汎用ICommandHandler<TCommand>インターフェイスを解決することでした。その側面に対する現在の解決策は次のとおりです。

Container = new UnityContainer().LoadConfiguration();

Container.ConfigureAutoRegistration()
    .ExcludeSystemAssemblies()
    .Include(type => type.ImplementsOpenGeneric(typeof(ICommandHandler<>)),
        (t, c) => c.RegisterType(typeof(ICommandHandler<>), t)
    )
    .ApplyAutoRegistration()
;

これは最初の部分で機能し、単一の ICommandHandler<TCommand>. これまでのところイライラすることが証明されているのは、装飾ハンドラーの実装です。デコレーターとして1 秒追加するとすぐにICommandHandler<TCommand>、Unity は StackOverflowException をスローします。Unity の内部についてはよくわかりませんが、どちらのインスタンスもICommandHandler<TCommand>インターフェイスを実装しているため、どちらのインスタンス (コマンド ハンドラーまたはコマンド ハンドラー デコレーター) に解決するかを判断できないためだと推測しています。

グーグルで調べたところ、最初にこの記事にたどり着きました。これは、私がブルートフォースメソッドと見なす方法でそれを行う方法を説明しています. これらの 関連 ページも見つけましたが、どれも私の問題に対する完全な解決策ではありません (そして、私は無知すぎて自分でそれを理解できません)。

その後、同じ懸念に対処しているように見えるこの記事を見つけました。ただし、beefy のソリューションでは、オープン ジェネリックの処理が考慮されていません。現在、ほとんどの依存関係は .config ファイルの unity セクションから読み込まれているため、ハンドラーやデコレーターごとに大量のコンパイル済みコードを書きたくありません。ある種の UnityContainerExtension と DecoratorBuildStrategy を使用するのが正しい方法のようですが、わかりません。私はビーフィーのコードでしばらく遊んでいて、完全に立ち往生しています。ジェネリックを考慮して彼のコードを変更しようとすると、BadImageFormatExceptions が発生しました (正しくない形式のプログラムを読み込もうとしました (HRESULT からの例外: 0x8007000B))。

デコレーター チェーンを実装するためにこれを行うというアイデアが気に入っています。これは短く、懸念ごとに 1 行しかないからです。

var container = new Container();

// Go look in all assemblies and register all implementations
// of ICommandHandler<T> by their closed interface:
container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>),
    AppDomain.CurrentDomain.GetAssemblies());

// Decorate each returned ICommandHandler<T> object with an
// TransactionCommandHandlerDecorator<T>.
container.RegisterDecorator(typeof(ICommandHandler<>),
    typeof(TransactionCommandHandlerDecorator<>));

// Decorate each returned ICommandHandler<T> object with an
// DeadlockRetryCommandHandlerDecorator<T>.
container.RegisterDecorator(typeof(ICommandHandler<>),
    typeof(DeadlockRetryCommandHandlerDecorator<>));

...しかし、必要がなければ、コンテナーを Unity から Simple Injector に変更したくありません。

だから私の質問は、ユニティ(プラス)を使用してオープンジェネリックデコレータチェーンを実装するにはどうすればよいUnityAutoRegistrationですか?

4

2 に答える 2

4

これは Unity で同等です。

// Go look in all assemblies and register all implementa-
// tions of ICommandHandler<T> by their closed interface:
var container = new UnityContainer();

var handlerRegistrations =
    from assembly in AppDomain.CurrentDomain.GetAssemblies()
    from implementation in assembly.GetExportedTypes()
    where !implementation.IsAbstract
    where !implementation.ContainsGenericParameters
    let services =
        from iface in implementation.GetInterfaces()
        where iface.IsGenericType
        where iface.GetGenericTypeDefinition() == 
            typeof(ICommandHandler<>)
        select iface
    from service in services
    select new { service, implementation };

foreach (var registration in handlerRegistrations)
{
    container.RegisterType(registration.service, 
        registration.implementation, "Inner1");
}

// Decorate each returned ICommandHandler<T> object with an
// TransactionCommandHandlerDecorator<T>.
container.RegisterType(typeof(ICommandHandler<>), 
    typeof(TransactionCommandHandlerDecorator<>),
    "Inner2",
    InjectionConstructor(new ResolvedParameter(
        typeof(ICommandHandler<>), "Inner1")));

// Decorate each returned ICommandHandler<T> object with an
// DeadlockRetryCommandHandlerDecorator<T>.
container.RegisterType(typeof(ICommandHandler<>), 
    typeof(DeadlockRetryCommandHandlerDecorator<>), 
    InjectionConstructor(new ResolvedParameter(
        typeof(ICommandHandler<>), "Inner2")));
于 2012-12-13T12:09:51.760 に答える
3

問題を正しく理解していることを願っています。これを機能させることに興味がありました。私は決してUnityの専門家ではありませんが、実装が少し簡単で実行も簡単なソリューションを考えていました別の容器で。オープン ジェネリックと他のタイプをサポートする唯一の方法は、2 つの別個のコンテナー (オープン ジェネリック用に 1 つ) とコマンド ハンドラー用に 1 つを用意することです。これは最善の方法ではないかもしれませんが、Unity では機能しました。また、他の人でも簡単になると思います。

だから私はこれを思いついた:

私は次のようにコンテナを作成しました(ハンドラーコンテナについてはまだ確信が持てない慣習的なアプローチを使用できます)

var container = new UnityContainer();

var container2 = new UnityContainer();

container2.RegisterType(typeof(ICommandHandler<QueryCommand>),
    typeof(QueryCommandHandler));
container.RegisterInstance("Handlers", container2);
container.RegisterInstance(container);
container.RegisterType(typeof(ICommandHandler<>),
    typeof(DecoratedHandler<>));

名前付きインスタンスとしてハンドラを含むコンテナ 2 が表示されます。

次に、パターンの要件に従って汎用ベース デコレータ クラスを作成しました。

public class DecoratorCommandHandler<TCommand>
    : ICommandHandler<TCommand>
{
    private ICommandHandler<TCommand> inner;
    public DecoratorCommandHandler(
        ICommandHandler<TCommand> inner)
    {
        this.inner = inner;
    }

    public virtual void Handle(TCommand command)
    {
         this.inner.Handle(command);
    }
}

次に、ソリューションに必要なすべての装飾をラップする別のジェネリック ハンドラーを作成しました。ここでは、TryCatch/Caching/Transactions の装飾、または各コマンド ハンドラーに適用したいものを追加します。

public class DecoratedHandler<TCommand>
    : DecoratorCommandHandler<TCommand>
{
    public DecoratedHandler(UnityContainer container)
        : base(BuildInner(container))
    {
    }

    private static ICommandHandler<TCommand> BuildInner(
        UnityContainer container)
    {
         var handlerContainer =
             container.Resolve<UnityContainer>("Handlers");
         var commandHandler =
             handlerContainer.Resolve<ICommandHandler<TCommand>>();

         return new TryCatchCommandHandler<TCommand>(commandHandler);
    }
}

最初の内部が、 、 、または詳細を処理するものなどQueryCommandHandler、要求したものに従って実際のコマンド ハンドラーを解決することに気付くでしょう。そして、それらすべてに共通したいすべてのデコレータでラップされます。UpdateCommandHandlerExecuteCommandHandler

次に、正しい方法で装飾された正しいハンドラーを解決できました。

ICommandHandler<QueryCommand> handler =
    container.Resolve<ICommandHandler<QueryCommand>>();

var cmd = new QueryCommand();

handler.Handle(cmd);

お役に立てれば

于 2012-03-28T05:26:48.370 に答える