在 EF Core 和 ASP.NET Core 中处理并发的最佳实践?

Iva*_*ono 5 c# asp.net-mvc entity-framework-core asp.net-core

目前,我在插入/更新实体时正在执行以下操作:

public async Task<IdentityResult> UpdateAsync(Model model, CancellationToken ct = default)
{
    var entity = await GetAsync(ct);
    if (entity == null)
    {
        return await CreateAsync(model, ct);
    }
    context.Entry(entity).State = EntityState.Detached;

    Update(model);
    model.ConcurrencyToken = Guid.NewGuid().ToString();

    await context.SaveChangesAsync();
    return IdentityResult.Success;
}
Run Code Online (Sandbox Code Playgroud)

以及更新方法:

protected virtual T Update(T entity)
{
    var dbEntityEntry = context.Entry(entity);
    if (dbEntityEntry.State == EntityState.Detached)
    {
        dbSet.Attach(entity);
        dbEntityEntry.State = EntityState.Modified;
    }

    return entity;
}
Run Code Online (Sandbox Code Playgroud)

这是有效的。但是有没有更好的(通用)方法来处理并发?

Cod*_*und 11

是的,EF Core 中有一种管理并发的通用方法。您已经通过添加一列来完成第一部分,该列将管理每次更新的行版本,但您正在手动更新并发令牌。

最好的方法是让数据库通过使用自动为您更新并发令牌Timestamp。如EF Core 文档中所述:

时间戳/行版本是每次插入或更新行时数据库自动生成新值的属性。该属性还被视为并发令牌,确保如果您正在更新的行自查询以来发生了更改,您会收到异常。精确的细节取决于所使用的数据库提供商;对于SQL Server,通常使用byte[]属性,它将被设置为数据库中的ROWVERSION列。

如何使用它(EF Core 文档中的示例):

public class Blog
{
    public int BlogId { get; set; }

    public string Url { get; set; }

    [Timestamp]
    public byte[] Timestamp { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

因此不必更新Timestamp或ConcurrencyCheck列,因为数据库会为您完成此操作。

最后一部分是确保正确处理并发冲突并让用户知道他/她已经过时了。如何管理并发冲突(它们引发异常)。您可以按照此处的说明进行操作。

另请查看本教程。

  • 新属性是“[ConcurrencyCheck]”。https://learn.microsoft.com/en-us/ef/core/modeling/concurrency?tabs=data-annotations (3认同)
  • `ConcurrencyCheck` 和 `TimeStamp` 是**不同的东西**。数据库本身支持“TimeStamp”,而“ConcurrencyCheck”必须在应用程序中手动更新。 (2认同)