2015-10-07 3 views
8

Я пытаюсь поручить моему приложению ASP.NET Core MVC использовать сторонний контейнер DI. Вместо написания адаптера я пытаюсь просто подключить библиотеку в соответствии с рекомендациями в this postЗамените активатор для промежуточного программного обеспечения в ASP.NET Core

Это работает очень хорошо - я могу заменить встроенный IControllerActivator своим собственным, который использует контейнер DI. Тем не менее, я сталкиваюсь с препятствием при попытке создания пользовательского промежуточного программного обеспечения, которое также зависит от вложенных зависимостей. ASP.NET не может разрешить эти зависимости, поскольку он не использует мой сторонний контейнер DI - есть ли эквивалент IControllerActivator для промежуточного программного обеспечения, или я застрял, используя встроенный DI или записывая адаптер?

** EDIT **

Вот еще некоторые из моего кода - Я на самом деле пытается использовать Ninject с использованием шаблона выше.

internal sealed class NinjectControllerActivator : IControllerActivator 
{ 
    private readonly IKernel _kernel; 

    public NinjectControllerActivator(IKernel kernel) 
    { 
     _kernel = kernel; 
    } 

    [DebuggerStepThrough] 
    public object Create(ActionContext context, Type controllerType) 
    { 
     return _kernel.Get(controllerType); 
    } 
} 

Я обнаружил, что у две проблемы:

  • Я не могу инъекционные стандартных компонентов ASP.NET в мой контроллерах, потому что Ninject не знает о них
  • Мой промежуточного слоя, что службы приложений не могут быть созданы, потому что ASP.NET не знает о Ninject.

Для примера первой проблемы, вот контроллер, который не может создать экземпляр, потому что я использую IUrlHelper (также обратите внимание на ILogger, который также не экземпляр):

public class SystemController : Controller 
{ 
    public SystemController(ILogger logger, IUrlHelper urlHelper) 
    { 
     /*...*/ 
    } 
} 

Вот пример второй задача с настраиваемым промежуточным слоем:

public class CustomMiddleware 
{ 
    private RequestDelegate _next; 
    // this is an application specific service registered via my Ninject kernel 
    private IPersonService _personService; 

    public CustomMiddleware(RequestDelegate next, IPersonService personService) 
    { 
     _next = next; 
     _personService = personService; 
    } 

    public async Task Invoke(HttpContext context) 
    { 
     /* ... */ 
    } 
} 

Я понимаю, что в теории компоненты ASP.NET должны быть в их собственном трубопроводе и мои компоненты приложения должны быть в ANO но на практике мне часто приходится использовать компоненты поперечным способом (как в приведенных выше примерах).

+0

Какой контейнер DI вы пытаетесь использовать? – Dealdiane

+0

@opiants выглядит как простой инжектор. –

+0

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

ответ

11

Твердых принципов диктует, что:

рефераты принадлежат слоям верхних/политиков (DIP)

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

Таким образом, вместо зависимости от абстракции Microsoft.Framework.Logging.ILogger, которая может соответствовать или не соответствовать нашим потребностям, принципы SOLID направляют нас к абстракциям (портам), которые принадлежат приложению, и используют реализации адаптеров, которые подключаются к фреймворку , Вот пример того, как может выглядеть your own ILogger abstraction.

Когда код приложения зависит от вашей собственной абстракции, вам необходима реализация адаптера, который будет иметь возможность направить вызов к реализации поставляемой рамок:

public sealed class MsLoggerAdapter : MyApp.ILogger 
{ 
    private readonly Func<Microsoft.Framework.Logging.ILogger> factory; 
    public MsLoggerAdapter(Func<Microsoft.Framework.Logging.ILogger> factory) { 
     this.factory = factory; 
    } 

    public void Log(LogEntry entry) { 
     var logger = this.factory(); 
     LogLevel level = ToLogLevel(entry.Severity); 
     logger.Log(level, 0, entry.Message, entry.Exception, 
      (msg, ex) => ex != null ? ex.Message : msg.ToString()); 
    } 

    private static LogLevel ToLogLevel(LoggingEventType severity) { ... } 
} 

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

container.RegisterSingleton<MyApp.ILogger>(new MsLoggerAdapter(
    app.ApplicationServices.GetRequiredService<Microsoft.Framework.Logging.ILogger>)); 

BIG ПРЕДУПРЕЖДЕНИЕ: Do не делать прямые копии каркасных абстракций. Это почти никогда не приведет к хорошим результатам. вы должны указать абстракции, которые определены в терминах вашего приложения. Это может даже означать, что адаптер становится более сложным и требует нескольких компонентов инфраструктуры для выполнения своего контракта, но это приводит к более чистым и более удобному программному коду.

Но если применение SOLID слишком много хлопот для вас, и вы просто хотите напрямую зависит от внешних компонентов, вы всегда можете перекрестную провод необходимые зависимости в контейнере приложения следующим образом:

container.Register<Microsoft.Framework.Logging.ILogger>(
    app.ApplicationServices.GetRequiredService<Microsoft.Framework.Logging.ILogger>); 

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

С промежуточным программным обеспечением здесь совершенно другая проблема. В вашем промежуточном программном обеспечении вы вводите данные времени выполнения (делегат next) в компонент (класс CustomMiddleware). Это дает вам двойное горе, потому что это усложняет регистрацию и устранение компонента и предотвращает его проверку и диагностирование контейнером. Вместо этого, вы должны переместить next делегата из конструктора и в Invoke делегата следующим образом:

public class CustomMiddleware 
{ 
    private IPersonService _personService; 

    public CustomMiddleware(IPersonService personService) { 
     _personService = personService; 
    } 

    public async Task Invoke(HttpContext context, RequestDelegate next) { /* ... */ } 
} 

Теперь вы можете подключить ваш межплатформенное в трубопровод следующим образом:

app.Use(async (context, next) => 
{ 
    await container.GetInstance<CustomMiddleware>().Invoke(context, next); 
}); 

Но не забыть, что вы всегда можете создать промежуточное программное обеспечение вручную следующим образом:

var frameworkServices = app.ApplicationServices; 

app.Use(async (context, next) => 
{ 
    var mw = new CustomMiddleware(
     container.GetInstance<IPersonService>(), 
     container.GetInstance<IApplicationSomething>(), 
     frameworkServices.GetRequiredService<ILogger>(), 
     frameworkServices.GetRequiredService<AspNetSomething>()); 

    await mw.Invoke(context, next); 
}); 

Это очень прискорбно, что ASP.NET называет свои собственные S ervices ApplicationServices, потому что здесь используется ваш собственный контейнер приложений; а не встроенная система конфигурации.

+1

Я понимаю принципы, которые нарушают счастливый путь, но альтернатива выглядит довольно ужасно. Следуя принципам письма, вы можете обернуть все абстракции вне вашего контроля и перекрестно подключить внешние зависимости к контейнеру приложения. Вы действительно думаете, что это лучше? – davidfowl

+2

@ davidfowl Я не предлагаю внешние внешние зависимости в контейнере приложения ВСЕ, потому что это означает, что компоненты приложения все еще могут зависеть от внешних зависимостей, в то время как они должны быть обернуты адаптерами. И не забывайте, что вам нужно только обернуть абстракции, которые использует ваше приложение, как правило, всего несколько. «Лучше ли это» зависит от размера и срока службы приложения, но в целом я бы сказал, что на самом деле это намного лучше. – Steven

+1

Хуже того, это означает, что вам нужно держать их вне контейнера приложения, получить их как-то и вручную передать их адаптеру. Это честно ужасно ИМО. Хорошая новость заключается в том, что эта картина выполнима, но не очень приятна для эстетического POV. – davidfowl

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

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