在ASP.NET 5中访问中间件中的DbContext

时间:2015-10-21 07:55:58

标签: c# entity-framework asp.net-core asp.net-core-mvc

我编写了我添加的自定义中间件

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在验证用户和密码时使用它。

3 个答案:

答案 0 :(得分:7)

如果您通过ApplicationDbContext方法注入SignInManager<ApplicationUser>Invoke该怎么办:

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。你真的需要吗?

答案 1 :(得分: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);
    }

答案 2 :(得分:0)

这是一个非常适合我的用例的简单解决方案。 我创建了一个简单的方法,我可以从应用程序的任何地方调用它来轻松获取数据库上下文:

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();

我在 Program.MyDbConnectionString 内设置了 public static string(一个 Startup.ConfigureServices() 字段)(这是一个通过 Program.Main()CreateHostBuilder(args).Build() 内调用的回调)。这样我就可以在应用程序的任何地方使用该连接字符串,而不必从 appsettings.json 或环境变量中重复检索它。