我正在使用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容器吗?
答案 0 :(得分:1)
没有办法从ASP.NET Core中完全删除内置DI容器,因为它完全集成在整个过程中;一切都取决于它的存在。
这意味着作为应用程序开发人员,无论如何,当涉及到更改默认行为时,您必须在某些时候与它进行交互。
但这并不意味着您强制使用内置DI容器,或实际上使用任何容器来构建对象图应用程序组件。在不使用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
确实包含一些可变状态,因此无法无限期缓存,您可以在委托中创建它。这意味着每次请求都会创建一次。