ASP.NET.Core 可以在没有 DI 容器的情况下使用吗
Can ASP.NET.Core be used without the DI Container
我正在使用 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 容器?
ASP.NETCore内置的DI Container没办法完全去掉,因为它完全集成在整个过程中;一切都取决于它的存在。该内置容器是 ASP.NET Core 提供的更大配置 API 的一部分。
这意味着,作为应用程序开发人员,在更改默认行为时,您将不得不以某种方式与它进行交互。
不过,这并不意味着您 被迫 使用内置 DI 容器,或者实际上使用 any 容器来构建应用程序 组件的对象图。在不使用 DI 容器的情况下构建对象图是一种非常常见的做法,称为 Pure DI,并且在大多数情况下,这在使用 ASP.NET Core 时也是可能的。
如果你想练习Pure DI,通常意味着更换一些常见的拦截点。一个这样的常见拦截点是 IControllerActivator
抽象。通过替换默认实现,您可以拦截 MVC 控制器实例的创建,这些实例通常是应用程序对象图的根对象。 Here is an example Github repository 演示了如何在创建控制器时应用 Pure DI。
但是,在您的示例中,您似乎只处理自定义中间件。那样的话,使用 Pure DI 就更简单了,因为它不需要替换工厂抽象,比如 IControllerActivator
。这可以按如下方式完成:
var middleware = new MyMiddleware(_dep1, _dep2);
app.Use((context, next) =>
{
return middleware.InvokeAsync(context, next);
});
请注意我是如何将 RequestDelegate
从 MyMiddleware
构造函数中移到 InvokeAsync
方法中的。这样做的原因是,可以独立于任何运行时值创建 MyMiddleware
。 RequestDelegate
是一个运行时值,在前面的示例中,MyMiddleware
仅在启动时创建一次。换句话说,它只是一个单例。
如果 MyMiddleware
确实包含一些可变状态,因此不能无限期缓存(例如因为它依赖于 DbContext),您可以在委托中创建它。这意味着它将根据请求创建一次。
我正在使用 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 容器?
ASP.NETCore内置的DI Container没办法完全去掉,因为它完全集成在整个过程中;一切都取决于它的存在。该内置容器是 ASP.NET Core 提供的更大配置 API 的一部分。
这意味着,作为应用程序开发人员,在更改默认行为时,您将不得不以某种方式与它进行交互。 不过,这并不意味着您 被迫 使用内置 DI 容器,或者实际上使用 any 容器来构建应用程序 组件的对象图。在不使用 DI 容器的情况下构建对象图是一种非常常见的做法,称为 Pure DI,并且在大多数情况下,这在使用 ASP.NET Core 时也是可能的。
如果你想练习Pure DI,通常意味着更换一些常见的拦截点。一个这样的常见拦截点是 IControllerActivator
抽象。通过替换默认实现,您可以拦截 MVC 控制器实例的创建,这些实例通常是应用程序对象图的根对象。 Here is an example Github repository 演示了如何在创建控制器时应用 Pure DI。
但是,在您的示例中,您似乎只处理自定义中间件。那样的话,使用 Pure DI 就更简单了,因为它不需要替换工厂抽象,比如 IControllerActivator
。这可以按如下方式完成:
var middleware = new MyMiddleware(_dep1, _dep2);
app.Use((context, next) =>
{
return middleware.InvokeAsync(context, next);
});
请注意我是如何将 RequestDelegate
从 MyMiddleware
构造函数中移到 InvokeAsync
方法中的。这样做的原因是,可以独立于任何运行时值创建 MyMiddleware
。 RequestDelegate
是一个运行时值,在前面的示例中,MyMiddleware
仅在启动时创建一次。换句话说,它只是一个单例。
如果 MyMiddleware
确实包含一些可变状态,因此不能无限期缓存(例如因为它依赖于 DbContext),您可以在委托中创建它。这意味着它将根据请求创建一次。