在 ASP.NET 5 的中间件中访问 DbContext
Posted
技术标签:
【中文标题】在 ASP.NET 5 的中间件中访问 DbContext【英文标题】:Accessing DbContext in Middleware in ASP.NET 5 【发布时间】:2015-10-21 07:55:58 【问题描述】:我编写了我添加的自定义中间件
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
在验证用户和密码时使用它。
【问题讨论】:
我有一些错误。请问你找到更正了吗? 你想达到什么目的? 【参考方案1】:如果你通过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
。你真的需要吗?
【讨论】:
我将如何从调用者传递这个数据库上下文? app.UseMyMiddleware(dbcontext) 之类的东西?【参考方案2】:此错误很可能发生,因为任何中间件都充当单例。您必须避免在中间件中使用成员变量。随意注入任务调用,但不要将注入值存储到成员对象中。
见:Saving HttpContext Instance in Middleware, Calling services in Middleware
我能够自己解决这个问题,方法是创建一个类,然后我可以将其传递给我的中间件中的其他方法:
public async Task Invoke(HttpContext context, IMetaService metaService)
var middler = new Middler
Context = context,
MetaService = metaService
;
DoSomething(middler);
【讨论】:
【参考方案3】:这是非常适合我的用例的简单解决方案。 我创建了一个简单的方法,我可以从应用程序的任何位置调用以轻松获取数据库上下文:
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
字段)(这是一个在Program.Main()
中通过CreateHostBuilder(args).Build()
调用的回调)。这样我就可以在应用程序的任何地方使用该连接字符串,而无需从 appsettings.json
或环境变量中重复检索它。
【讨论】:
只是为了解释反对意见......这不是一个好的模式。您正在反对 asp.net 核心的依赖注入模式。几乎在任何你想使用 DbContext 的地方,它都应该通过构造函数或参数注入来注入。这包括 Startup.Configure 方法、中间件、控制器、页面、组件等...... @burton,您能否分享一个指向帖子、教程或任何显示它正在“通过构造函数或参数注入”的链接? DI 概述:docs.microsoft.com/en-us/aspnet/core/fundamentals/… 找不到关于动作方法参数注入的文章/文档,但您可以使用动作方法参数上的 [FromServices] 属性在控制器动作中注入服务方法。 我应该说“构造函数、属性或参数注入”。 谢谢你,@burton!我期待着本周晚些时候将这些知识“注入”我的大脑! :)以上是关于在 ASP.NET 5 的中间件中访问 DbContext的主要内容,如果未能解决你的问题,请参考以下文章
ASP.NET Core中app.UseRouting()和app.UseEndpoints()区别