Unity IOC Recursion - использование для перехватчиков? - PullRequest
1 голос
/ 04 октября 2011

Итак, я пытаюсь решить проблему, с которой, я уверен, уже столкнулся кто-то другой. По сути, я хочу, чтобы вызов моего контейнера IoC рекурсивно разрешал зависимости, но также потенциально мог выполнять некоторый пользовательский код для изменения результата на основе набора предопределенных критериев. Это расплывчато, поэтому позвольте мне привести пример:

Скажем, у меня есть контроллер вроде:

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

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

У меня также есть несколько тестовых версий этого контроллера (скажем, я его реорганизовал, и я хочу убедиться, что он не работает ужасно в Prod при тестировании 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 ]

1 голос
/ 04 октября 2011

Предполагая, что вы можете написать ABEmailSenderFactory методом Create ...

В Autofac это будет так же просто, как

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

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

Я не знаю много оЕдинство, но похоже, это не так просто .

1 голос
/ 04 октября 2011

Я бы попытался поменять контейнер Unity на время запроса.Определите, что это ваше состояние "V2", затем создайте новый контейнер, но с другим набором зарегистрированных типов.Используйте это в рамках этого запроса.Не было бы хорошей идеей создавать новый контейнер при каждом запросе в производстве, но это должно быть хорошей идеей для тестирования.

0 голосов
/ 06 октября 2011

Есть несколько вариантов, которые вы можете использовать здесь.Я предполагаю, что Unity 2.0 здесь, это было сложнее в более ранних версиях.

Вы можете заранее рассчитать различные перестановки и использовать переопределения преобразователя в вызове разрешения:

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

Это заменит разрешенный объект для SampleType, где бы он ни находился в дереве, но для этого требуется непосредственно экземпляр.

Вы можете использовать именованные регистрации и выполнить набор регистраций NxM для каждой комбинации факторов, которые вы A/ B тестирование против.Хотя после 4 изменений это становится действительно грязным.

Вы можете использовать инъекционную фабрику для вещей, с которыми вы тестируете - фабрика может выяснить, какую из них использовать:

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

Это будетвыполните переключение во время выполнения на основе того, что делает метод figureOutWhichType.

Третьим вариантом будет расширение контейнера, которое добавляет стратегию отображения типов для выполнения логики внутри цепочки разрешения.

Из нихвариантов, я бы, вероятно, начал с заводского подхода и перешел бы к пользовательскому расширению, если что-то выходит из-под контроля.

0 голосов
/ 04 октября 2011

Я думаю, что я просто собираюсь пойти по пути принятия собственного решения.По сути, мне пришлось взломать текущий 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 через записи базы данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...