【问题标题】:EF core error with transaction but not using trasactionsEF核心错误与事务但不使用事务
【发布时间】:2022-01-19 10:18:00
【问题描述】:

最近我们的一位测试人员在我们的应用程序上报告了一个错误。 此错误似乎与 EF 核心事务有关,但我们没有在代码中使用事务。但是我们在这个应用上根本没有使用事务。

当多个用户连接到我们的应用程序时,此问题似乎是随机发生的。

错误是:

Error : System.invalidOperationException
BeginExecuteReader require the command to have a transaction when the connection assigned to the command is in a pending local transaction. 
The transaction property of the command has not been initialized.

从堆栈跟踪来看,当我们简单地在一个名为“SurveyOperations”的类中执行此操作时会发生此错误:

Survey surveyToSave = await _context.Surveys.FindAsync(id);

详细说明:

_context 在 SurveyOperations 构造函数中使用 asp.net core 依赖注入进行初始化。

在 startup.cs 中,SurveyOperations 的范围是“瞬态”,数据库连接也是如此。 我们 100% 的 EF 核心调用是异步的。

我们在使用 OwningComponentBase 注入 SurveyOperations 的 Blazor 组件上遇到此错误:

@using Microsoft.Extensions.DependencyInjection
@inherits OwningComponentBase

@code{
   private SurveyOperations _surveyOperations;
   private Survey survey;

   protected override async Task OnInitializedAsync()
   {
       _surveyOperations = ScopedServices.GetRequiredService<SurveyOperations>();
      survey = await _surveyOperations.GetSurveyAsync(id);
   }

   private async Task HandleValidSubmit()
   {
      // Get sone data from the form on the component

      await _surveyOperations.SaveSurvey(survey);

   }
}
```

We suspect that EF core is reusing connections but we didn't know how to avoid that.

【问题讨论】:

  • GetSurveyAsync 是 _surveyOperations 上的第一个操作吗?
  • 不完全是,我简化了代码(可能太多了),我更新了我的示例代码。其实这个组件是一个小表单,提交时我调用_surveyOperations.SaveSurvey(survey)。此方法将使用 findAsync 从数据库加载调查,以在更新数据库之前执行一些检查。
  • Survey surveyToSave = await _context.Surveys.FindAsync(id); 的目的是什么?您是否使用跟踪实体、延迟加载?

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


【解决方案1】:

在我看来,您的设计是错误的,使用 OwningComponentBase 并不能保护您免受在 Blazor 中使用 DbContext 引起的问题。您可以使用以下方法解决此问题:

@inherits OwningComponentBase<AppDbContext>

AppDbContext 是你的 DbContext

或者更好,更适合您的设计,实现 IDbContextFactory...在这种情况下,您可以像这样配置 dbcontext:

builder.Services.AddDbContextFactory<ContactContext>(opt =>
    opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db"))

看这里完整的解释:full explanation

在您的SurveyOperations 服务中,您可以执行以下操作:

public SurveyOperations(IDbContextFactory<ContactContext> factory)
{
    DbFactory = factory;
}

在您的各种方法中,您可以执行以下操作:

using var _context = DbFactory.CreateDbContext();    

Survey surveyToSave = await _context.Surveys.FindAsync(id);

请注意,第二个选项会使OwningComponentBase 的使用变得多余。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-08
    • 1970-01-01
    • 2021-11-02
    相关资源
    最近更新 更多