【问题标题】:A second operation started on this context before a previous operation completed Asp.net EF Core在前一个操作完成之前,在此上下文上启动了第二个操作 Asp.net EF Core
【发布时间】:2020-11-06 14:50:33
【问题描述】:

我有一个小问题,我一直在使用页面上的视图组件,但我一直遇到以下问题。我正在使用 asp.net core 3.1 和 ef core 3.1

处理请求时数据库操作失败。 InvalidOperationException:在此上下文中启动了第二个操作 在之前的操作完成之前。这通常是由 不同的线程使用相同的 DbContext 实例。更多 有关如何避免 DbContext 线程问题的信息,请参阅 https://go.microsoft.com/fwlink/?linkid=2097913。有待处理 MISDBContext 的模型更改在 Visual Studio 中,使用 Package 管理器控制台为这些更改构建新的迁移,并 将它们应用于数据库:

PM> Add-Migration [迁移名称] PM> Update-Database 或者, 您可以搭建一个新的迁移并从命令提示符处应用它 你的项目目录:

dotnet ef 迁移添加 [迁移名称] dotnet ef 数据库更新

我已经检查了 SO,它建议您现在将以下内容添加到您的课程中,我认为我根本不需要第二个上下文是正确的,这是导致我的问题的原因。我加了一行

ServiceLifetime.Transient

显然,以上行允许更多并发连接到上下文。

services.AddDbContext<MISDBContext>
 (options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Transient);

services.AddDbContext<ApplicationDbContext>(options =>
 options.UseSqlServer(
 Configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Transient);            

这是我的视图组件的典型布局,我认为实际上它可能是导致问题的原因,因为我试图在访问上下文的同时访问用户存储。

 private Task<ApplicationUser> GetCurrentUserAsync() => _userManager.GetUserAsync(HttpContext.User);
    public async Task<Guid> GetCurrentTennantId() {
        ApplicationUser usr = await GetCurrentUserAsync();
        TempData["TeannantId"] = usr?.Id;
        Guid.TryParse(usr?.Id, out Guid resultTennantId);
        return resultTennantId;
    }
    private Task<List<ApplicationUser>> GetItemsAsync() {
        string currentUser =  GetCurrentTennantId().Result.ToString();
        var excludeCurrentUser= _userManager.Users.Where(w => w.Id != currentUser).ToListAsync();
        return excludeCurrentUser;
    }
}

【问题讨论】:

  • _userManager 使用哪个 DbContext?您是否同时使用两个 dbContexts?另外,您真的需要两个 dbContext 用于同一个数据库吗?一些有趣的阅读here。
  • 它使用 MISDBContext 是我删除有问题的问题的问题。
  • 在我看来,要解决这个问题,您应该确保在调用 GetCurrentUserAsync 和 GetCurrentTennantId 方法时始终立即等待异步调用。

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


【解决方案1】:

显然,以上行允许更多并发连接到上下文。

没有;它为每个请求创建一个新的上下文。每个上下文一次只能用于一个操作。因此,例如,如果您的上下文以前是一个单例,那么一次只有一个 HTTP 请求可以进行操作。

我认为事实上这可能是导致问题的原因,因为我试图在访问上下文的同时访问用户存储。

此代码本身不会导致问题,但如果此模式在您的代码库中重复出现,则可能会导致问题。

总之,use async and await for all Task&lt;T&gt;-returning methods, unless the method is trivial。例如,GetCurrentUserAsync 是微不足道的(它只是传递给另一种方法),所以可以忽略 async 和 await 那里。 GetItemsAsync 绝对不是小菜一碟,所以应该使用async 和await:

private async Task<List<ApplicationUser>> GetItemsAsync() {
  string currentUser = (await GetCurrentTennantId()).ToString();
  return await _userManager.Users.Where(w => w.Id != currentUser).ToListAsync();
}

如果您以类似的方式在您的代码库中应用 async 和 await,您应该解决并发上下文问题。

【讨论】:

    猜你喜欢
    • 2018-11-07
    • 1970-01-01
    • 2020-09-30
    • 2022-01-01
    • 1970-01-01
    • 2019-05-25
    • 1970-01-01
    • 2018-07-23
    • 2020-05-11
    相关资源
    最近更新 更多