Search code examples
c#entity-frameworkasp.net-mvc-5aspnetboilerplateoptimistic-concurrency

Unable to catch DbUpdateConcurrencyException and throw UserFriendlyException


I am using ASP.NET Boilerplate with Code-First Entity Framework and MVC 5. I want to handle concurrency. In the Update method, I put Timestamp data annotation for RowVersion field in my entity.

In the manager of my entity and in Update operation, I am trying to catch DbUpdateConcurrencyException exception and throw UserFriendlyException but because UpdateAsync is asynchronous, I don't know where to handle the exception.

Where should I handle this exception to be user-friendly and not to see Internal Server Error?

public abstract class BaseFullAuditedEntity : FullAuditedEntity<Guid>
{
    [Timestamp]
    public byte[] RowVersion { get; set; }
}

public class Branch : BaseFullAuditedEntity
{
    public string Name { get; set; }
}

Manager code:

public interface IBranchManager : IDomainService
{
    Task<Branch> Update(Branch branch, byte[] RowVersion);
}

public class BranchManager : DomainService, IBranchManager
{
    private IRepository<Branch, Guid> _branchRepository { get; }

    public async Task<Branch> Update(Branch branch, byte[] RowVersion)
    {
        try
        {
            return await _branchRepository.UpdateAsync(branch);
        }
        catch (DbUpdateConcurrencyException ex)
        {
            throw new UserFriendlyException("Update Concurrency Happened");
        }
    }
}

Solution

  • UpdateAsync(branch) only adds branch to the context.

    Inject IUnitOfWorkManager and await SaveChangesAsync():

    try
    {
        await _branchRepository.UpdateAsync(branch);
        await _unitOfWorkManager.Current.SaveChangesAsync(); // Add this
        return branch;
    }
    catch (DbUpdateConcurrencyException ex)
    {
        throw new UserFriendlyException("Update Concurrency Happened");
    }
    

    Alternatively, override SaveChanges and SaveChangesAsync in your DbContext to catch for all entities:

    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            throw new UserFriendlyException("Update Concurrency Happened");
        }
    }
    
    public override async Task<int> SaveChangesAsync()
    {
        try
        {
            return await base.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            throw new UserFriendlyException("Update Concurrency Happened");
        }
    }