Как изменить регистрацию зависимостей во время выполнения с помощью простого инжектора?

Я использую структуру IoC Simple Injector и хочу иметь возможность изменять регистрацию зависимостей во время выполнения. . Например, у меня есть две реализации, A и B, интерфейса I. Реализация A регистрируется при запуске приложения, но в зависимости от некоторого флага, который может меняться во время выполнения, я хотел бы переключить реализацию. В настоящее время мы делаем это с событием OnActionExecuting нашего BaseController, от которого наследуются все наши контроллеры. Вот пример кода того, что я пытаюсь сделать.

protected override void OnActionExecuting(
    ActionExecutingContext filterContext)
{
    if (IsRuntimeFlag)
    {
        // check current implementation type and 
        // change implementation to A
    }
    else
    {
        // check current implementation type and 
        // change implementation to B
    }

    base.OnActionExecuting(filterContext);
}

Заранее спасибо за вашу помощь.


person Will    schedule 23.08.2012    source источник
comment
Вам нужно сделать это вручную? Как насчет использования инфраструктуры IOC Container?   -  person Thiago Custodio    schedule 23.08.2012
comment
Я бы рекомендовал комбинировать фабрику с шаблоном стратегии, а не использовать для этого контейнер внедрения. Используйте Контейнер для разрешения вашего графа объектов на ранней стадии. См. Корень композиции.   -  person TrueWill    schedule 23.08.2012


Ответы (1)


В случае, если IsRuntimeFlag является значением конфигурации (поэтому не может измениться в течение срока службы приложения), вы можете выполнить регистрацию следующим образом:

if (IsRuntimeFlag)
{
    container.Register<I, A>();
}
else
{
    container.Register<I, B>();
}

или равно:

container.Register(typeof(I), IsRuntimeFlag ? typeof(A) : typeof(B));

В случае, если значение может измениться во время жизни приложения, прокси-сервер или композит, отвечающий за отправку к правильному экземпляру является правильным решением:

public sealed class RuntimeFlagIComposite : I
{
    private readonly A a;
    private readonly B b;

    public RuntimeFlagIComposite(A a, B b) {
        this.a = a;
        this.b = b;
    }

    void I.Method() => this.Instance.Method();

    private I Instance => IsRuntimeFlag ? this.a : this.b;
}

Поскольку композит напрямую зависит от A и B, вы можете просто зарегистрировать его следующим образом:

container.Register<I, RuntimeFlagIComposite>();

Если время жизни A или B отличается от временного (новый экземпляр для каждого запроса), их также следует зарегистрировать. Например:

container.Register<A>(Lifestyle.Singleton);
container.Register<B>(Lifestyle.Scoped);

Вы также можете позволить вашему композиту зависеть от самой абстракции I вместо конкретных реализаций A и B:

public class RuntimeFlagIComposite : I
{
    private I a;
    private I b;

    public RuntimeFlagIComposite(I a, I b)
    {
        this.a = a;
        this.b = b;
    }
}

В зависимости от абстракции I этот класс становится более гибким и тестируемым. Однако это означает, что вам нужно зарегистрировать его немного иначе:

container.Register<I, RuntimeFlagIComposite>();

container.RegisterConditional<I, A>(c => c.Consumer.Target.Name == "a");
container.RegisterConditional<I, B>(c => c.Consumer.Target.Name == "b");
person Steven    schedule 23.08.2012
comment
Спасибо за ответ, однако этот флаг неизвестен на момент регистрации контейнера и может меняться на протяжении всего жизненного цикла приложения. Я думаю, что могу установить глобальный флаг и изменить его позже во время приложения, но я не думаю, что это чистый подход к этому. При ближайшем рассмотрении я думаю, что составной шаблон может работать в этом случае. Еще раз спасибо за помощь. - person Will; 24.08.2012
comment
Привет Стивен, Просто интересно, если у вас есть другие идеи. При попытке реализовать это я понял, что флаг также зависит от вещей на уровне веб-приложения, в частности, от значений файлов cookie, в отличие от уровня службы, где будет композит. Следовательно, композит не будет иметь доступа к флагу. Еще раз спасибо заранее. - person Will; 10.09.2012
comment
Я думаю, вы сами ответили на свой вопрос. Если композит будет зависеть от конкретных вещей пользовательского интерфейса, он должен быть определен на уровне пользовательского интерфейса. Поскольку интерфейс определен на более низком уровне, совершенно нормально определить его на уровне пользовательского интерфейса (или вы даже можете сделать его частью корня композиции). - person Steven; 10.09.2012