EF Core,在此上下文中开始的第二个操作,Context Inhertiance重用连接

时间:2018-01-30 14:22:31

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

我正在尝试了解如何创建具有两个生命周期的上下文,我正在运行异步代码,我需要将我的上下文分开,以便它们不会在相同的连接下运行,EntityFramework但是,似乎重用了连接单独的上下文,导致此错误:

  

System.InvalidOperationException:'在上一个操作完成之前,在此上下文中启动了第二个操作。

在我的StartUp我注册两个上下文:

string connection = @"Server=localhost;Database=Custom;Trusted_Connection=True;";

services.AddEntityFrameworkSqlServer().AddDbContext<CustomContext>(
                options => options.UseSqlServer(connection))
        // I've also tried without this, it's weird but it still maps
        .AddDbContext<IsolatedCustomContext>(
                options => options.UseSqlServer(connection));

services.AddTransient<DbContext, CustomContext>();
services.AddTransient<IsolatedCustomContext>();

我的孤立上下文与自定义上下文相同,所以我声明如下:

public class IsolatedCustomContext : CustomContext
{
    public IsolatedCustomContext(DbContextOptions<CustomContext> options,
        DbAuthorizationOptions<CustomContext> authorizationOptions, 
        IMemoryCache memoryCache) : base(options, authorizationOptions, memoryCache)
    {
    }
}

和我的自定义上下文声明如下:

public partial class CustomContext: DbContext
{
    public CustomContext(DbContextOptions<CustomContext> options,
         DbAuthorizationOptions<CustomContext> authorizationOptions, IMemoryCache memoryCache) : base(options, authorizationOptions, memoryCache)
    {
    }

    public virtual DbSet<User> Users { get; set; }
}

在我的Startup我将用户个人资料与我的身份服务器同步如下:

o.JwtBearerEvents = new JwtBearerEvents
{
    OnTokenValidated = async tvc => { await AuthenticationRule.ValidateToken(tvc); }
};

我的身份验证使用隔离的上下文来更新用户信息:

// Using shouldn't be required but I've tried with it and without it
using (var context = serviceProvider.GetRequiredService<IsolatedAstootContext>())
{
    var userId = await context.Users
                              .Where(u => u.UniqueIdentifier == subjectId)
                              .Select(x => x.Id)
                              .FirstOrDefaultAsync();
}

看起来像Pipeline Split并且在令牌验证后同时运行以执行请求我有一个看起来像这样的控制器:

[Route("api/[controller]")]
public UsersController : Controller
{
     CustomContext _context;

     public UsersController(CustomContext context)
     {
          this._context = context;
     }

     [HttpGet]
     public User GetCurrentUser()
     {
         var userId = User.GetUserIdFromClaims();
         return this._context.Users.Where(x => x.Id == userId).FirstOrDefaultAsync();
     }
}

当我调用GetCurrentUser时,我得到并发错误,我可以在调用堆栈中看到它还没有退出隔离的上下文。奇怪的是,它们是两个独立的背景。

是否有可能通过继承上下文,EntityFrameWork正在查看此上下文,以及如何重用连接?

有谁知道我如何注册这些,以便他们可以在不同的连接上并行运行?

1 个答案:

答案 0 :(得分:1)

我在实体框架Core GitHub上做了一些挖掘,发现了这个issue

看起来这是由DbOptions通过继承引起的,为了解决这个问题我需要隐藏受保护的构造函数

public partial class CustomContext : DbContext
{
    public CustomContext(DbContextOptions<CustomContext> options,
         DbAuthorizationOptions<CustomContext> authorizationOptions, IMemoryCache memoryCache)
         : base(options, authorizationOptions, memoryCache)
    {
    }

    protected CustomContext(DbContextOptions options,
         DbAuthorizationOptions<CustomContext> authorizationOptions, IMemoryCache memoryCache)
         : base(options, authorizationOptions, memoryCache)
    {
    }
}

然后在我可以从我的隔离上下文中调用受保护的构造函数:

public class IsolatedContext : CustomContext
{
    public IsolatedAstootContext(DbContextOptions<IsolatedCustomContext> options,
        DbAuthorizationOptions<CustomContext> authorizationOptions, 
        IMemoryCache memoryCache) : base(options, authorizationOptions, memoryCache)
    {
    }
}