项目

并发检查

概述

并发检查(也称为并发控制)是指用于在存在并发更改时(多个进程或用户同时访问或修改数据库中的同一数据)确保数据一致性的特定机制。

常用的并发控制机制/方法有两种:

  • 乐观并发控制:允许多个用户尝试更新同一条记录,而不会通知用户其他人也在尝试更新该记录。

    • 如果某个用户成功更新了记录,其他用户需要获取该记录的最新更改才能进行修改。
    • 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会显示如下图所示的用户友好错误信息。

乐观并发

在本文档中