我有问题。我有一个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
上。
我知道问题出在并行解决上,但我不知道如何解决。有什么想法吗?
答案 0 :(得分:5)
DbContext不是线程安全的(这是您所收到的异常告诉您的信息),并且调用DbContext.Set<T>.Add()
不会花费大量时间。通过并行化Add()
,您不异步地向数据库添加多个实体-您只是在调用SaveChanges()
时将这些实体标记为要添加的实体< / em>。
因此,尽管我确定您有理由并行处理_db.Save(answer)
调用,但它可能并没有任何性能改进,因此您可以将其完全删除,从而对工作进行序列化。
如果您在那里进行的工作确实受益于并行化,只需将调用移至DbContext.Set<T>.Add()
那里-这不是线程安全的。