并发检查
概述
并发检查(也称为并发控制)是指用于在存在并发更改时(多个进程或用户同时访问或修改数据库中的同一数据)确保数据一致性的特定机制。
常用的并发控制机制/方法有两种:
乐观并发控制:允许多个用户尝试更新同一条记录,而不会通知用户其他人也在尝试更新该记录。
- 如果某个用户成功更新了记录,其他用户需要获取该记录的最新更改才能进行修改。
- ABP的并发检查系统采用乐观并发控制。
悲观并发控制:通过锁定机制防止同时更新记录。更多信息请参阅 此处。
使用方法
IHasConcurrencyStamp 接口
要为实体类启用并发控制,应直接或间接实现 IHasConcurrencyStamp 接口。
public interface IHasConcurrencyStamp
{
public string ConcurrencyStamp { get; set; }
}
- 这是并发控制的基础接口,仅包含一个名为
ConcurrencyStamp的简单属性。 - 当创建新记录时,如果实体实现了
IHasConcurrencyStamp接口,ABP会自动为 ConcurrencyStamp 属性设置唯一值。 - 当更新记录时,ABP会比较实体的 ConcurrencyStamp 属性与用户提供的 ConcurrencyStamp 值,如果匹配,则自动使用新的唯一值更新 ConcurrencyStamp 属性。如果不匹配,则抛出
AbpDbConcurrencyException异常。
如果存在工作单元,需要在创建或更新时调用 SaveChangesAsync 方法以获取生成的
ConcurrencyStamp。
示例:为Book实体应用并发控制
为实体实现 IHasConcurrencyStamp 接口:
public class Book : Entity<Guid>, IHasConcurrencyStamp
{
public string ConcurrencyStamp { get; set; }
//...
}
同时,让输出和更新DTO类实现 IHasConcurrencyStamp 接口:
public class BookDto : EntityDto<Guid>, IHasConcurrencyStamp
{
//...
public string ConcurrencyStamp { get; set; }
}
public class UpdateBookDto : IHasConcurrencyStamp
{
//...
public string ConcurrencyStamp { get; set; }
}
在应用服务的 UpdateAsync 方法中,将输入的 ConcurrencyStamp 值设置到实体:
public class BookAppService : ApplicationService, IBookAppService
{
//...
public virtual async Task<BookDto> UpdateAsync(Guid id, UpdateBookDto input)
{
var book = await BookRepository.GetAsync(id);
book.ConcurrencyStamp = input.ConcurrencyStamp;
//将其他输入值设置到实体...
//使用 autoSave: true 获取最新的 ConcurrencyStamp
await BookRepository.UpdateAsync(book, autoSave: true);
}
}
- 此后,当多个用户尝试同时更新同一条记录时,会发生并发标记不匹配,并抛出
AbpDbConcurrencyException异常。
基类
聚合根实体类已实现 IHasConcurrencyStamp 接口。因此,如果从以下任一基类派生,无需手动实现 IHasConcurrencyStamp 接口:
AggregateRoot,AggregateRoot<TKey>CreationAuditedAggregateRoot,CreationAuditedAggregateRoot<TKey>AuditedAggregateRoot,AuditedAggregateRoot<TKey>FullAuditedAggregateRoot,FullAuditedAggregateRoot<TKey>
示例:为Book实体应用并发控制
可以让实体继承自基类之一:
public class Book : FullAuditedAggregateRoot<Guid>
{
//...
}
然后,让输出和更新DTO类实现 IHasConcurrencyStamp 接口:
public class BookDto : EntityDto<Guid>, IHasConcurrencyStamp
{
//...
public string ConcurrencyStamp { get; set; }
}
public class UpdateBookDto : IHasConcurrencyStamp
{
//...
public string ConcurrencyStamp { get; set; }
}
在应用服务的 UpdateAsync 方法中,将输入的 ConcurrencyStamp 值设置到实体:
public class BookAppService : ApplicationService, IBookAppService
{
//...
public virtual async Task<BookDto> UpdateAsync(Guid id, UpdateBookDto input)
{
var book = await BookRepository.GetAsync(id);
book.ConcurrencyStamp = input.ConcurrencyStamp;
//将其他输入值设置到实体...
//使用 autoSave: true 获取最新的 ConcurrencyStamp
await BookRepository.UpdateAsync(book, autoSave: true);
}
}
此后,当多个用户尝试同时更新同一条记录时,会发生并发标记不匹配,并抛出 AbpDbConcurrencyException 异常。您可以手动处理该异常,或让ABP自动处理。
如果不手动处理异常,ABP会显示如下图所示的用户友好错误信息。
抠丁客



