【问题标题】:.NET Core Entity Framework - asynchronous writting into database.NET Core Entity Framework - 异步写入数据库
【发布时间】:2020-05-28 22:59:11
【问题描述】:

我有问题。我有 ASP .NET Core REST API 应用程序,并且在一种方法中,我试图将更多更改异步写入数据库。每次将不同数量的对象写入数据库时​​,每次出现三种不同错误之一。任何建议可以错了吗?

这是我的代码:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(connection_string), ServiceLifetime.Transient);
services.AddScoped<IHelper, Helper>();
...
}

Helper.cs

private MyDbContext _dbContext;
public Helper(IOptions<HelperSettings> settings, ILogger<Helper> logger, MyDbContext dbContext)
{
    ...
        _dbContext = dbContext;
    ...
}

public void Save(object entity)
{
    ...
        _dbContext.Add(entity);
}

这是引发异常的控制器和方法。

public class MyController : ControllerBase
{
private readonly Helper _db;

public MyController(IHelper helper)
{
      _db = helper;
}
...

[HttpPost]
[Route("something")]
[Produces("application/json")]
public async Task<ActionResult<Guid>> CreateSomethingAsync([FromBody] DataRequest data)
{
    ...
          if (data.Answers != null)
          {
                List<Task> saveTasks = new List<Task>();

                foreach (AnswerData ans in data.Answers)
                {
                    Answer answer = ans.ConvertToAnswer(); //just create new Answer instance and filll it with data from AnswerData
                    saveTasks.Add(Task.Run(() => _db.Save(answer)));
                }

                await Task.WhenAll(saveTasks);
                await _db.DbContext.SaveChangesAsync();
          }
          return Ok(...);
}
}

我在另一个应用程序中循环调用CreateSomethingAsync()。它会引发以下三个异常之一:

System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'

System.InvalidOperationException: 'Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.'

System.InvalidOperationException: Cannot start tracking InternalEntityEntry for entity type 'Answer' because another InternalEntityEntry is already tracking the same entity

在线_dbContext.Add(entity);我的Helper.cs

我知道问题出在并行性上,但我不知道如何解决。有什么想法吗?

【问题讨论】:

  • 你为什么在Task.Run里面打电话给_db.SaveDbContext 不是线程安全的。
  • Add 不对数据库做任何事情,它只是在上下文中添加一个 entity 以进行跟踪。 SaveChangesAsync 保留所有更改。这是一件GOOD 的事情 - 它为您提供工作单元、断开连接的操作、只需 调用SaveChanges 即可回滚更改的能力。这个Save 方法没有任何作用,也不需要在后台调用。这是瞬间的
  • 我怀疑整个 Helper 类是试图实现一个“存储库”,EF 本身已经提供了一些东西 - DbSet 本质上是一个存储库

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


【解决方案1】:

DbContext 不是线程安全的(这就是你得到的异常告诉你的),调用DbContext.Set&lt;T&gt;.Add() 不会花费大量时间。通过并行化Add(),您不会异步地将多个实体添加到数据库中——您只是在调用SaveChanges()时将实体标记为待添加。

因此,虽然我确信您有理由并行化您的 _db.Save(answer) 调用,但它可能没有任何性能改进,因此您可以完全删除它,将工作序列化。

如果您在其中所做的工作确实受益于并行化,只需将调用移至 DbContext.Set&lt;T&gt;.Add() 即可 - 它不是线程安全的。

【讨论】:

  • 最重要的是,不要在请求中使用Task.Run,你不会得到任何东西。 ASP.NET Core 的经验法则:请求线程上的 CPU 绑定工作,异步/等待(异步)I/O 绑定任务。
猜你喜欢
  • 2017-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-10
  • 1970-01-01
  • 2017-03-21
  • 1970-01-01
相关资源
最近更新 更多