4

Я использую Simple Injector с проектом ASP.NET Web API, и я хочу использовать разные реализации для одного интерфейса в зависимости от используемой версии конечной точки REST. Например, если используется v1 конечной точки, IPaymentData должен быть создан с классом PaymentData, но если используется v2, то IPaymentData должен быть реализован классом PaymentDataNew. Это оказывается проблематичным!Регистрация смены простого инжектора во время выполнения

Я не хочу использовать составной класс, как часть документации Simple Injector, потому что это означает, что мой код должен быть в курсе и учитывать инфраструктуру внедрения, которую я использую (плохой).

Я заметил в последней версии (3.0) что-то, что называется "Context based injection". Используя функцию container.RegisterConditional, должно быть возможно запустить делегат каждый раз, когда тип будет разрешен.

container.RegisterConditional(typeof(IPaymentData), 
    c => ((HttpContext.Current.Items["version"] as string ?? "1") == "2") 
     ? typeof(PaymentDataNew) 
     : typeof(PaymentData), 
    Lifestyle.Scoped, 
    c => true); 

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

Есть ли что-то, что мне не хватает? Как я могу убедиться, что делегат вызывается каждый раз, когда приходит запрос?

ответ

5

, поскольку это означает, что мой код должен знать и принимать во внимание рамки инъекции я использую

Не совсем. Составной шаблон является хорошо известным и часто используемым шаблоном, поэтому его определение в коде не делает ваш код зависимым от библиотеки DI. И если вы не хотите определять композит в коде приложения, вы всегда можете указать его внутри своего Composition Root. Корень композиции уже имеет очень сильную зависимость от библиотеки DI, поэтому там должны быть размещены любые материалы, специфичные для библиотеки.

Использование контейнера.RegisterConditional функция должна иметь возможность запускать делегат каждый раз при разрешении типа.

Простой инжектор v3's RegisterConditional метод позволяет применять регистрацию условно на основе статической информации. Это означает, что поставленный предикат называется только конечным количеством времени (обычно один раз за потребляющий компонент). Методы (IntelliSense/XML) документация states:

предикат будет оцениваться только конечное число раз; предикат не подходит для принятия решений на основе условий выполнения.

Это не вызываться каждый раз, когда вы ее решения. Причина этого заключается в два раза:

  1. Это оптимизирует производительность, так как решение, принятое в предикате может быть сожжено в скомпилированном выражение, но, что более важно,
  2. Это предотвращает вас от времени выполнения решений во время построение графа объектов.

Форма вашего графа объекта не должно зависеть от параметров среды выполнения (например, version в вашем HttpContext). Это усложняет граф объектов и делает очень трудным verify и diagnose графом объекта для правильности.

Решение, которое я бы посоветовал, заключается в реализации реализации прокси для IPaymentData, что позволяет сделать коммутатор во время выполнения. Это не Простая инжекторная реализация; вы должны стремиться к простым, статическим и проверяемым объектным графам независимо от используемой библиотеки DI.

Это как такое прокси-сервер может выглядеть следующим образом:

public sealed class HttpRequestVersionSelectorPaymentData : IPaymentData 
{ 
    private readonly PaymentData paymentOld; 
    private readonly PaymentDataNew paymentNew; 
    public VersionSelectorPaymentData(PaymentData old, PaymentDataNew paymentNew) { ... } 

    private bool New => HttpContext.Current.Items["version"] as string ?? "1") == "2"; 
    private IPaymentData PaymentData => this.New ? paymentNew : paymentOld; 

    // IPaymentData method(s) 
    public Payment GetData(int id) => this.PaymentData.GetData(id); 
} 

Хотя это вполне возможно сделать во время выполнения решений при строительстве объекта графа (путем регистрации делегата), я настоятельно совет против делать это, по причинам, указанным выше.

+1

Спасибо, @Steven :). Мое понимание вашего превосходного инжектора зависимости является рудиментарным, но постепенно улучшается. Престижность в документации также! – Torfi

Смежные вопросы