ASP.NET Core可以不使用DI容器吗?

7
我正在使用ASP.NET.Core将Web服务器嵌入到一个庞大的遗留桌面应用程序中。我的中间件组件需要引用预先存在的应用程序对象。
通过使用本地DI容器,我费了很大力气才让它工作,但最终的代码非常晦涩难懂。
我真正想做的是显式地通过构造函数参数注入依赖项(即特定的预先存在的对象实例)。 DI容器的自动魔法没有给我任何好处,只有很多痛苦!
是否可能在不使用DI容器的情况下使用ASP.NET.Core?
以下是一些简化的代码以说明我的当前解决方案:
    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);
        }
    }

启动和应用程序代码:

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

这个示例代码能否进行重构以消除DI容器的使用?


1
你能否提供更多细节,说明你希望通过构造函数参数进行配置的内容是什么?这些是控制器还是其他东西?而且,你是否有多个相同类型的实例,希望在不同的位置传递? - Jon Skeet
4
没问题。只需通过new关键字创建实例即可。 - OlegI
这似乎是一个XY问题 - Nkosi
1
为什么不使用 DI 容器让生活更轻松呢? - Konrad
2
@Konrad: 因为DI容器并不总是能让你的生活更轻松。不管它们是否能够让生活更加便捷,这取决于很多因素。例如,请查看thisthis以获取更多信息。而.NET中的依赖注入第2版的第12章节则对此进行了更详细的讨论。 - Steven
显示剩余2条评论
1个回答

3

无法完全删除ASP.NET Core中内置的DI容器,因为它完全集成在整个过程中;一切都依赖于它的存在。该内置容器是ASP.NET Core提供的更大配置API的一部分。

这意味着作为应用程序开发人员,以某种方式,您将不得不与其交互,当涉及到更改默认行为时。 然而,这并不意味着您被迫使用内置的DI容器,或者实际上使用任何容器来构建应用程序组件的对象图。构建对象图而不使用DI容器是一种相当常见的做法,称为Pure DI,在使用ASP.NET Core时,这在很大程度上也是可能的。

如果您希望练习Pure DI,通常意味着替换几个常见的拦截点。其中一个常见的拦截点是IControllerActivator抽象层。通过替换默认实现,您可以拦截创建MVC控制器实例的过程,这些实例通常是应用程序对象图的根对象。这里是一个示例Github存储库,演示了如何使用Pure DI来创建控制器。
然而,在您的示例中,似乎只涉及自定义中间件。在这种情况下,使用Pure DI更加简单,因为它不需要替换工厂抽象,比如IControllerActivator。可以按照以下方式完成:
var middleware = new MyMiddleware(_dep1, _dep2);

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

注意我如何将RequestDelegateMyMiddleware构造函数中移动到InvokeAsync方法中。这样做的原因是使得可以独立于任何运行时值创建MyMiddlewareRequestDelegate是一个运行时值,在之前的示例中,MyMiddleware只在启动时创建一次。换句话说,它只是一个单例。
如果MyMiddleware包含一些可变状态,因此不能无限期地缓存(例如因为它依赖于DbContext),则可以在委托内部创建它。这意味着它将每个请求创建一次。

嘿,Steven,这可能有点脱离语境,但我看到了你的SimpleInjector库,我想知道相比于.NET Core内置的IoC容器,它是否提供了任何优势? - Konrad
我只是想知道在我的基于.NET Core的项目中替换它是否是一个好主意,或者什么时候替换它才有意义。 - Konrad
1
嗨Konrad,第三方容器是否有用取决于您的需求。但是,这个答案可能会提供一些见解。此答案 - Steven
@Steven 'Pure DI'!! 原来是这样叫的。我已经做了接近15年了。现在它有一个好名字,真不错。 - MetaMapper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接