7

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?

Steven
  • 166,672
  • 24
  • 332
  • 435
MetaMapper
  • 968
  • 8
  • 22
  • 1
    Could you give more details of what you want to be configured via constructor parameters? Are these controllers, or something else? And do you have multiple instances of the same type that you want to be passed in different places? – Jon Skeet May 31 '18 at 09:35
  • 4
    Sure you can. Simply create instances through `new` keyword – OlegI May 31 '18 at 09:43
  • This appears to be an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). – Nkosi May 31 '18 at 09:50
  • Yes you can absolutely use ASP.NET Core without a DI Container, but how that would look like in your case depends on what you actually need. if th Can you show more code that demonstrates your problem? – Steven May 31 '18 at 10:39
  • @Steven I've added some sample code to my question, which is much less 'obtuse and opaque' than my original solution. I'd still like to know how to do this without the DI container though. – MetaMapper May 31 '18 at 12:11
  • 1
    Why making your life harder without di container? – Konrad May 31 '18 at 14:12
  • 2
    @Konrad: Because DI Containers not always make your life easier. Whether or not they make life easier or not, depends on a lot of factors. See, for instance, [this](https://blog.ploeh.dk/2012/11/06/WhentouseaDIContainer/) and [this](https://simpleinjector.org/blog/2015/12/when-should-you-use-a-container/) for more information. And chapter 12 of [Dependency Injection in .NET, second edition](https://manning.com/seemann2/) contains a more detailed discussion about this. – Steven Jun 04 '18 at 15:27

1 Answers1

3

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.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • Hey Steven, this might be a bit out of context but I saw your SimpleInjector library and I wonder does it offer any benefits when compared to .NET Core's built-in IoC container? – Konrad May 31 '18 at 12:47
  • I just wonder if it'd be good to replace it in my .NET Core based project or when would it make sense to do it. – Konrad May 31 '18 at 12:49
  • 1
    Hi Konrad, whether or not a third-party container is useful depends on your needs. But [this answer](https://stackoverflow.com/questions/30681477/) might give some insights. – Steven May 31 '18 at 12:54
  • @Steven 'Pure DI'!! So that's what it's called. It's what I've been doing for close to 15 years. It's nice it's got a good name now. – MetaMapper May 31 '18 at 13:58