1

だから、私は他の誰かがすでに遭遇したと確信している問題を解決しようとしています. 基本的に、IoC コンテナーへの呼び出しで依存関係を再帰的に解決する必要がありますが、カスタム コードを実行して、事前定義された一連の基準に基づいて結果を変更する可能性もあります。ややこしいので、例を挙げます。

次のようなコントローラーがあるとします。

 public class SampleController : Controller
 {
      protected SampleType _sampleType = null;

      public SampleController(SampleType sampleType)
      {
          _sampleType = sampleType;
      }
 }

このコントローラーのテスト バージョンもいくつかあります (たとえば、リファクタリングを行い、AB テストで露出をテストして、本番環境でひどく壊れないようにしたいとします)。

 public class SampleController_V2 : SampleController
 {
      protected SampleType _sampleType = null;
      protected AnotherType _anotherType = null;

      public SampleController_V2(SampleType sampleType, AnotherType anotherType)
      {
          _sampleType = sampleType;
          _anotherType = anotherType;
      }
 }

DefaultControllerFactoryコントローラーを作成するときに Unity を使用するように拡張しました。これはすべて正常に機能しています。今、私がやりたいことは、解決すべきことがあれば、階層内の特定のタイプを AB テストする機能を提供することです。これはトップ レベルではうまく機能しますが、オブジェクト グラフを再帰的に処理するため、子要素では機能しません。

現在、適切なコントローラーを選択して解決し、依存関係を与えます。ただし、依存関係への個々の呼び出しをインターセプトして、それらを AB テストすることもできないようです。データベース構成を通じてテストを定義し、IOC コンテナーに基準に基づいてテストを解決させることができます。例:

SessionIds that start with the letter 'a': SampleController_V2
Everyone Else                            : SampleController
UserIds ending in 9                      : SampleType_V2
Everyone Else                            : SampleType

これはすべてトップレベルのアイテムで機能します。ただし、への呼び出し_unityContainer.Resolve(type)は再帰呼び出しではないようです。タイプを解決しようとするときに、そのコードを任意のポイントに挿入できるようにしたいと思います。

-> Attempt to Resolve SampleController
    -> Test Code Tells Us to use _V2 if possible. 
    -> Attempt to Resolve SampleType
       -> Test Code tells us to use the _V1 if possible.
    -> Resolves SampleType_V1
    -> Attempt to Resolve AnotherType
       -> No Test Defined, Use the Default
    -> Resolves AnotherType
-> Resolves SampleController_V2 (passing SampleType_V1 as the dependency and then AnotherType as the other dependency) 

いくつかのオンライン記事を見ると、ある種の Unity インターセプターを使用する必要があるように思えますが、この時点で、ある種のテスト アーキテクチャが組み込まれた独自の IoC コンテナーを作成しているようです。

うまくいけば、コンストラクターを見つけて各タイプを再帰的に解決するという苦痛に陥る前に、誰かがこれを行う方法について良い考えを持っていることを願っています.

編集: したがって、各依存関係のコンストラクターパラメーターを再帰的に検査して独自のインジェクションを作成することは、実際にはそれほど恐ろしいことではありませんが、独自のカスタムソリューションのために Unity を捨てると、上司が少し動揺する可能性があると思います.

4

4 に答える 4

1

メソッドでを記述できると仮定するABEmailSenderFactoryCreate...

Autofacでは、次のように簡単です

builder.RegisterType<ABEmailSenderFactory>().AsSelf();

builder.Register(c => c.Resolve<ABEmailSenderFactory>().Create())
    .As<EmailSender>();

Unity についてはよくわかりませんが、それほど簡単ではないようです。

于 2011-10-04T19:46:53.140 に答える
1

リクエストの期間中、Unity コンテナーを交換しようとします。それが「V2」状態であることを検出し、新しいコンテナーを作成しますが、異なるタイプのセットが登録されています。そのリクエストの範囲でそれを使用します。本番環境でリクエストごとに新しいコンテナを作成するのは良い考えではありませんが、テストには良い考えです。

于 2011-10-04T18:29:24.957 に答える
0

カスタムソリューションを作成する道をたどるつもりだと思います。実際、私は自分が望む効果を得るために現在使用しているIoCをハックする必要がありました。これは、自分でソリューションを作成するよりもはるかに優れているわけではありません。依存関係の単純な解決以外のほとんどの機能にIoCを使用していないので、IoCを使用できないかどうかはわかりません。私は次の方法で終わりました:

public virtual object Resolve(Type requestedType, Session session = null, RequestContext requestContext = null)
{
    ConstructorInfo constructor = requestedType.GetConstructors().First();
    object[] dependencies = null;
    Type resultType = requestedType;

    if (session == null || requestContext == null)
    {
        dependencies = constructor.GetParameters().Select(parameter => Resolve(parameter.ParameterType)).ToArray();
    }
    else
    {
        InitializeTestingArchitecture();

        var testVersion = _testingProvider.GetTestVersionFor(requestedType, requestContext, session);

        if(testVersion == null)
            return Resolve(requestedType);

        resultType = _testTypeLoader.LoadTypeForTestVersion(requestedType, testVersion);
        constructor = resultType.GetConstructors().First();

        dependencies = constructor.GetParameters().Select(parameter => Resolve(parameter.ParameterType, session, requestContext)).ToArray();
    }

    return Activator.CreateInstance(resultType, dependencies);
}

このようにして、データベースレコードを介してABクラスインスタンスの公開を制御できます。

于 2011-10-04T19:53:57.973 に答える
0

There are a couple of options you can use here. I'm assuming Unity 2.0 here, this was harder in earlier versions.

You could pre-calculate the various permutations ahead of time and use resolver overrides in the resolve call:

container.Resolve(figureOutControllerType(),
    new DependencyOverride<SampleType>(figureOutSampleType()));

This will replace the resolved object for SampleType wherever it appears in the tree, but it does require an instance directly.

You could use named registrations and do the NxM set of registrations for each combination of factors you're A/B testing against. Although that gets really messy after about 4 changes.

You could use an injectionfactory for things you're testing against - the factory could figure out which one to use:

container.RegisterType<SimpleType>(
    new InjectionFactory((c) => container.Resolve(figureOutWhichType()));

This will do a runtime switch based on whatever the figureOutWhichType method does.

A third option would be a container extension that adds a type-mapping strategy to do the logic inside the resolve chain.

Of these options, I'd probably start with the factory approach, and move to the custom extension if things get way out of hand.

于 2011-10-05T21:56:02.873 に答える