Replace Activator for Middleware in ASP.NET Core

I am trying to instruct my ASP.NET Core MVC application to use a third-party DI container. Instead of writing an adapter, I'm trying to just connect the library as recommended by this post

This works very well - I can replace the built-in IControllerActivator with my own, which uses a DI container. However, I encounter an obstacle when trying to create custom middleware that also depends on nested dependencies. ASP.NET cannot resolve these dependencies because it does not use my third-party DI container - is there an IControllerActivator equivalent for middleware, or am I sticking with the built-in DI or adapter adapter?

** EDIT **

Here is my code too - I'm actually trying to use Ninject using the template above.

 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); } } 

I found that I have two problems:

  • I cannot embed standard ASP.NET components in my controllers because Ninject does not know about them
  • My middleware that uses application services cannot be created because ASP.NET does not know about Ninject.

For an example of the first problem, here is a controller that fails to instantiate, because I use IUrlHelper (also pay attention to ILogger , which also fails to instantiate):

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

Here is an example of a second problem with custom middleware:

 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) { /* ... */ } } 

I understand that in theory, ASP.NET components should be in their own pipeline, and the components of my application should be in another, but in practice I often have to use components in a transverse way (as in the examples above).

+8
c # dependency-injection asp.net-core ninject
source share
1 answer

SOLID principles dictate that:

abstracts belong to upper / political strata ( DIP )

This means that our application code should not depend directly on the framework code, even if it is an abstraction. Instead, we must define the role interfaces that are intended to be used by our application.

Thus, instead of depending on the abstraction of Microsoft.Framework.Logging.ILogger , which might or may not correspond to our specific needs of the application, SOLID principles direct us to the abstractions (ports) that belong to this application and use adapter implementations, which connect to the code infrastructure. Here is an example of how your own ILogger abstraction might look.

When the application code depends on your own abstraction, you need an adapter implementation that can redirect the call to the implementation provided by the framework:

 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) { ... } } 

This adapter can be registered in the application container as follows:

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

GREAT WARNING . Do not make direct copies of the frame abstraction. This will almost never lead to good results. You must specify the abstractions that are defined in terms of your application. It may also mean that the adapter is becoming more complex and requires several infrastructure components to fulfill its contract, but it leads to a cleaner and more convenient application code.

But if using SOLID is too much trouble for you, and you just want to directly depend on external components, you can always cross-link the necessary dependencies in the application container as follows:

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

It's just as simple, but note that in order for your application to be clean and maintainable, it is much better to define specific application abstractions that are consistent with SOLID principles. Also note that even if you do, you will only need some of these cross-wire dependencies. Therefore, it is better to keep the application container as separate as possible from the vNext configuration system.

Using middleware here is a completely different problem. In middleware, you enter runtime data ( next delegate) into the component ( CustomMiddleware class). This gives you double grief, because it complicates the registration and elimination of the component and prevents it from being checked and diagnosed by the container. Instead, you should move the next delegate from the constructor and to the Invoke delegate as follows:

 public class CustomMiddleware { private IPersonService _personService; public CustomMiddleware(IPersonService personService) { _personService = personService; } public async Task Invoke(HttpContext context, RequestDelegate next) { /* ... */ } } 

Now you can connect the middleware to the pipeline as follows:

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

But do not forget that you can always create your middleware manually as follows:

 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); }); 

Not surprisingly, ASP.NET calls its own ApplicationServices because it uses your own application container; rather than an embedded configuration system.

+11
source share

All Articles