在 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方法注入ApplicationDbContextSignInManager&lt;ApplicationUser&gt;会怎样:

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.MyDbConnectionStringpublic 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 核心中间件中访问 ModelState

ASP.NET 5 中的 Web API 身份验证

[四] ASP.NET Core中间件

ASP.NET Core中app.UseRouting()和app.UseEndpoints()区别

如何在 ASP.NET MVC 5 中访问 AspNetUserRoles 表

如何在 ASP.NET Core 中获取没有构造函数注入的注入服务的实例?