'Best practice to handle concurrency in EF Core and ASP.NET Core?

Currently I'm doing the following when inserting/updating and entity:

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;
}

And the update method:

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;
}

This is working. But is there a better (generic) way to handle concurrency?



Solution 1:[1]

Yes, there is a generic way to manage concurrency in EF Core. You already did the first part by adding a column that will be manage the row version for each update but you're updating the concurrency token manually.

The best way is to let the DB to automatically update the concurrency token for you by using Timestamp. As explained in EF Core documentation:

A timestamp/rowversion is a property for which a new value is automatically generated by the database every time a row is inserted or updated. The property is also treated as a concurrency token, ensuring that you get an exception if a row you are updating has changed since you queried it. The precise details depend on the database provider being used; for SQL Server, a byte[] property is usually used, which will be set up as a ROWVERSION column in the database.

How to use it (sample from EF Core documentation):

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

    public string Url { get; set; }

    [Timestamp]
    public byte[] Timestamp { get; set; }
}

So don't have to update the Timestamp or ConcurrencyCheck column because it will be done for you by the database.

The last part is to make sure to correctly handle the concurrency conflicts and let the user know that he/she is out of date. How to manage concurrency conflicts (they raise exception). You can follow what is explained here.

Also check this tutorial.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 George Fabish