Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can ASP.NET.Core be used without the DI Container

I am using ASP.NET.Core to embed a web server into a large legacy desktop application. My middleware components need to reference pre-existing application objects.

With difficulty I have got this working using the native DI container, but the resulting code is extraordinarily obtuse and opaque.

What I would really like to do, is to explicitely inject the dependencies, which are specific pre-existing object instances, through constructor parameters. The auto-magic of the DI container isn't giving me any benefits, just a lot of pain!

Is it possible to use ASP.NET.Core without the DI Container?

Here's some simplified code to illustrate my current solution:

    class Dependency 
    { 
        public string Text { get; } 
        public Dependency(string text) => Text = text; 
    }

    class MyMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly Dependency _dep1;
        private readonly Dependency _dep2;

        public MyMiddleware(RequestDelegate next, Dependency dep1, Dependency dep2)
        {
            _next = next;
            _dep1 = dep1;
            _dep2 = dep2;
        }

        public Task InvokeAsync(HttpContext context)
        {
            return context.Response.WriteAsync(_dep1.Text + _dep2.Text);
        }
    }

Startup and application code:

    class Startup
    {
        private readonly Dependency _dep1;
        private readonly Dependency _dep2;

        public Startup(Dependency dep1, Dependency dep2)
        {
            _dep1 = dep1;
            _dep2 = dep2;
        }

        public void Configure(IApplicationBuilder appBuilder)
        {
            appBuilder.UseMiddleware<MyMiddleware>(_dep1, _dep2);
        }
    }

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            var dep1 = new Dependency("Hello ");
            var dep2 = new Dependency("World");
            int port = 5000;
            StartWebServer(port, dep1, dep2);
            Process.Start($"http://localhost:{port}");
        }

        void StartWebServer(int port, Dependency dep1, Dependency dep2)
        {
            IWebHostBuilder builder = new WebHostBuilder();
            builder.UseUrls($"http://0.0.0.0:{port}/");
            builder.UseKestrel();
            builder.ConfigureServices(servicesCollection => servicesCollection.AddSingleton(new Startup(dep1, dep2)));
            builder.UseStartup<Startup>();
            IWebHost webHost = builder.Build();
            var task = webHost.StartAsync();
        }
    }

Can this sample code be refactored to eliminate the DI container?

like image 920
MetaMapper Avatar asked May 31 '18 09:05

MetaMapper


1 Answers

There is no way to completely remove the built-in DI Container from ASP.NET Core, since it’s completely integrated in the whole process; everything depends on its existence. That built-in container is part of the larger configuration API that ASP.NET Core provides.

This means that as application developer, in one way or another, you will have to interact with it at some point, when it comes to changing default behavior. This doesn't mean, though, that you are forced to use the built-in DI Container, or in fact use any container, to build up object graphs of application components. Building object graphs without the use of a DI Container is a quite common practice called Pure DI, and this is, for the most part, possible as well when using ASP.NET Core.

If you wish to practice Pure DI, it typically means replacing a few common interception points. One such common interception point is the IControllerActivator abstraction. By replacing the default implementation, you can intercept the creation of MVC controller instances, which are typically the root objects of your application's object graphs. Here is an example Github repository that demonstrates how to apply Pure DI with respect to creating controllers.

In your example, however, you only seem to deal with custom middleware. In that case, using Pure DI is even simpler, because it doesn't require replacing factory abstractions, such as IControllerActivator. This can be done as follows:

var middleware = new MyMiddleware(_dep1, _dep2);

app.Use((context, next) =>
{
    return middleware.InvokeAsync(context, next);
});

Notice how I moved the RequestDelegate out of the MyMiddleware constructor into the InvokeAsync method. Reason for doing this, is that makes it possible to create MyMiddleware independently of any runtime values. RequestDelegate is a runtime value and in the previous example, MyMiddleware is just created once at startup. In other words, it's simply a Singleton.

In case MyMiddleware does contain some mutable state, and therefore can’t be cached indefinitely (for instance because it depends on a DbContext), you can create it inside the delegate. This means it will be created once per request.

like image 180
Steven Avatar answered Sep 28 '22 04:09

Steven