推荐直接在Invoke/InvokeAsync 函数中进行依赖注入
public async Task Invoke(HttpContext context,DataContext data)
一个很尴尬的事情是,在我学习asp.net core 的书中没有介绍在中间件中使用数据库的介绍,甚至是例子都没有。
但是我写的一个小项目中却需要在中间件中使用数据库上下文来进行数据库查询。
当我直接在中间件的构造函数中使用依赖注入来使用数据库上下文时,会报错。
一些关于什么Scope的东西,今天问了其他方面,有些解释。
如果直接在中间件的构造函数中注入数据库上下文,可能会导致一些问题。这是因为中间件的构造函数在应用程序启动时就会被调用,而不是在每个请求处理过程中调用。数据库上下文是一个瞬态(transient)服务,它在每个请求中应该是唯一的。
在上面的代码中,我们使用了
IServiceProvider
来获取一个作用域,并从作用域中获取DataContext
的实例。这样可以确保在每个请求中都创建一个新的数据库上下文实例,并在请求结束时适当地清理和释放资源。通过使用作用域来获取数据库上下文,我们遵循了每个请求一个数据库上下文的最佳实践。这样可以避免在多个请求之间共享同一个上下文实例可能导致的并发和数据一致性问题。
因此,使用
IServiceProvider
创建作用域,并从作用域中获取数据库上下文实例是一种可行的方式,可以在中间件中安全地使用数据库上下文。
大致可以理解是,中间件的构造只会在每个作用链的当前结点构造一次。
这一次构造会处理多个通过的http请求。而,DataContext的注入需要作用域(一般是一个请求)。
我之前是这样处理的。不要这样做
这是一个链条
app.Map("/v1", apiApp =>
{
apiApp.UseMiddleware<RosterMiddleware>();
apiApp.UseMiddleware<SecureMiddleware>(app.Services);
apiApp.UseMiddleware<RequestChooserMiddleware>();
apiApp.UseMiddleware<PricingMiddleware>(app.Services);
apiApp.UseEndpoints(endpoints => { endpoints.MapPost("v1/{*path}", new TransferEndpoint().Endpoint); });
});
其中中间件SecureMiddleware和PricingMiddleware需要用到数据库上下文来进行数据库操作。
以SecureMiddleware为例。
构造函数和基本属性如下
public class SecureMiddleware
{
private DynamicTable _table;
private readonly RequestDelegate _next;
private readonly IServiceProvider _provider;
public SecureMiddleware(RequestDelegate next,IServiceProvider provider,DynamicTable table)
{
_provider = provider;
_next = next;
_table = table;
}
在Invoke/InvokeAsync中这样使用
public async Task Invoke(HttpContext context)
{
using var scope = _provider.CreateScope();
await using var data = scope.ServiceProvider.GetRequiredService<DataContext>();
data就是一个DataContext类型的对象,紧接着就可以使用DataContext来进行数据库操作了。
这样是可行的,但是在我进行测试的时候。
await context.Response.WriteAsync("Secure "+data.GetHashCode());
在使用数据库上下文的中间件分别输出,然后在处理程序中进行输出,他们的哈希值不一样,也就是他们是属于不同的对象。
紧接着我又测试了下面的使用方式。可行,并且输出的哈希值是一样的。
app.Map("/v1", apiApp =>
{
apiApp.UseMiddleware<TestMiddleware>();
apiApp.UseMiddleware<RosterMiddleware>();
apiApp.UseMiddleware<SecureMiddleware>();
apiApp.UseMiddleware<ModelFilterMiddleware>();
apiApp.UseMiddleware<RequestChooserMiddleware>();
apiApp.UseMiddleware<PricingMiddleware>();
apiApp.UseEndpoints(endpoints =>
{ endpoints.MapPost("v1/{*path}", new TransferEndpoint().Endpoint); });
});
test 36736776Secure 36736776Process 36736776Pricing 36736776
是直接在Invoke/InvokeAsync 函数 中进行依赖注入。
这样使用,推荐的方式
public async Task Invoke(HttpContext context,DataContext data)
这样的话,在一个http请求,会共用一个数据库上下文对象,(这里是DataContext)。
并且,上下文对象会自己进行销毁(框架实现的效果)