在中间件中访问DbContext.净5
本文关键字:DbContext 访问 中间件 | 更新日期: 2023-09-27 18:18:41
我编写了我的自定义中间件,我添加在
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//...
app.UseAutologin();
app.UseMvc(routes =>
{
//...
所以它是Mvc开始发挥作用之前的最后一个中间件。
在我的中间件的Invoke
方法中,我想(间接)访问DbContext
。
public async Task Invoke(HttpContext context)
{
if (string.IsNullOrEmpty(context.User.Identity.Name))
{
var applicationContext = _serviceProvider.GetService<ApplicationDbContext>();
var signInManager = _serviceProvider.GetService<SignInManager<ApplicationUser>>();
var result = await signInManager.PasswordSignInAsync(_options.UserName, _options.Password, true, false);
}
await _next(context);
}
几乎每次我得到以下异常:
在
InvalidOperationException
:尝试使用上下文当它被配置时。不能使用DbContext
实例OnConfiguring
内部,因为它仍然在配置中。
现在,这显然是由PasswordSignInAsync
方法引发的。但是我如何确保在做这些事情之前已经创建了模型呢?
也许我没有完全清楚:我不想自己使用DbContext
- PasswordSignInAsync
在验证用户和密码时使用它。
如果通过Invoke
方法注入ApplicationDbContext
和SignInManager<ApplicationUser>
会怎么样:
public async Task Invoke(HttpContext context, ApplicationDbContext applicationContext, SignInManager<ApplicationUser> signInManager)
{
if (string.IsNullOrEmpty(context.User.Identity.Name))
{
var result = await signInManager.PasswordSignInAsync(_options.UserName, _options.Password, true, false);
}
await _next(context);
}
这样可以从正确的作用域解析服务。我注意到你实际上没有在任何地方使用ApplicationDbContext
,只使用SignInManager
。你真的需要吗?
这个错误很可能发生,因为任何中间件都充当单例。您必须避免在中间件中使用成员变量。可以随意注入到Task Invoke中,但不要将注入值存储到成员对象中。
参见:在中间件中保存HttpContext实例,调用中间件中的服务
我自己能够解决这个问题,通过创建一个类,然后我可以传递到我的中间件中的其他方法:
public async Task Invoke(HttpContext context, IMetaService metaService)
{
var middler = new Middler
{
Context = context,
MetaService = metaService
};
DoSomething(middler);
}
就这样:-
public async Task Invoke(HttpContext context)
{
var dbContext = context.RequestServices.GetRequiredService<ClinicDbContext>();
await _next(context);
}
这是一个简单的解决方案,非常适合我的用例。我创建了一个简单的方法,可以从应用程序中的任何地方调用,以轻松获取数据库上下文:
public class UtilsApp
{
public static MyDbContext GetDbContext()
{
DbContextOptionsBuilder<MyDbContext> opts =
new DbContextOptionsBuilder<MyDbContext();
optionsBuilder.UseSqlServer(Program.MyDbConnectionString); // see connection string below
return new MyDbContext(opts.Options);
}
}
然后,在应用程序的任何地方使用:
MyDbContext dbContext = UtilsApp.GetDbContext();
我从Startup.ConfigureServices()
中设置Program.MyDbConnectionString
(public static string
字段)(这是通过CreateHostBuilder(args).Build()
从Program.Main()
中调用的回调)。这样我就可以在应用程序的任何地方使用该连接字符串,而不必从appsettings.json
或环境变量中反复检索它。