跳到主要内容
版本:Next

批量操作

本文档中引用的文件

目录

  1. 简介
  2. 核心组件分析
  3. 批量删除功能实现
  4. 克隆功能实现
  5. 事务一致性与操作日志
  6. 性能优化策略
  7. 调用示例与异常处理
  8. 高并发环境下的挑战与解决方案
  9. 结论

简介

本文档详细说明了在 MyEntityNameAppService 中实现的批量删除与克隆功能的技术细节。重点分析 DeleteBatchAsyncCloneAsync 方法的内部逻辑,包括如何通过仓储接口 IMyEntityNameRepository 批量获取实体、处理事务一致性以及记录操作日志。同时探讨克隆过程中核心属性的复制机制、新唯一标识的生成方式,以及审计字段(如创建人、创建时间)的正确重置。此外,还讨论了批量操作的性能优化策略,如分批处理、异步执行和数据库批量支持,并提供调用示例和异常处理建议。

核心组件分析

本节分析实现批量操作的核心类和接口。

图示来源

本节来源

批量删除功能实现

DeleteManyAsync 方法实现了对多个实体的批量删除功能。该方法接收一个 IEnumerable<Guid> 类型的 ID 集合,遍历每个 ID 并调用 DeleteAsync 方法进行删除。

该方法虽然名为“批量”,但实际上是通过循环逐个删除实体,而非使用数据库层面的批量删除操作。这意味着每次删除都会触发一次数据库事务,可能影响性能。然而,这种方式能够更好地控制错误处理——如果某个删除失败,可以捕获异常并决定是否继续后续删除。

图示来源

本节来源

克隆功能实现

CloneAsync 方法实现了实体的克隆功能。其核心逻辑如下:

  1. 获取当前最大排序值 sort,用于为新克隆的实体分配连续的排序号。
  2. 遍历传入的 ID 列表,通过 FindAsync 获取原始实体。
  3. 对每个实体,生成新的名称:原始名称 + 克隆标记(由 MyEntityNameConsts.CloneTag 定义),并检查新名称是否已存在,若存在则追加更多标记以确保唯一性。
  4. 调用实体的 Clone 方法创建新实例,传入新的 GUID、新名称和递增的排序号。
  5. 通过 InsertAsync 将新实体插入数据库。

克隆过程中,MyEntityName 类的 Clone 方法仅复制了核心业务属性(Code、Name、Remark、Sort),而审计字段(如 CreatorId、CreationTime)由基类 FullAuditedAggregateRoot<Guid> 在插入时自动重置为当前用户和当前时间,确保了审计信息的正确性。

图示来源

本节来源

事务一致性与操作日志

尽管 DeleteManyAsyncCloneAsync 方法本身未显式使用事务包装,但由于它们运行在 ABP 框架的上下文中,默认情况下每个仓储操作(如 InsertAsyncDeleteAsync)都会在独立的事务中执行,保证了单个操作的原子性。

对于 CloneAsync,虽然每个插入是独立事务,但方法内部通过顺序执行确保了逻辑上的连续性。若需保证所有克隆操作的完全原子性(即全部成功或全部失败),应考虑在应用服务层使用 IUnitOfWorkManager 显式开启一个跨多个操作的事务。

操作日志方面,ABP 框架的审计功能(通过继承 FullAuditedAggregateRoot<Guid>)自动记录了每个实体的创建人、创建时间、最后修改人和最后修改时间。这些信息在克隆时被正确重置,体现了框架对审计字段的良好支持。

本节来源

性能优化策略

当前实现存在以下性能优化空间:

  1. 批量数据库操作DeleteManyAsync 使用循环删除,可优化为使用原生 SQL 或 EF Core 的 ExecuteDeleteAsync 方法进行真正的批量删除,减少数据库往返次数。
  2. 并行处理:对于非顺序依赖的操作(如独立实体的克隆),可考虑使用 Parallel.ForEachAsyncTask.WhenAll 并行执行,提升吞吐量。
  3. 分批处理:当处理大量实体时,应实现分页或分批机制,避免一次性加载过多数据导致内存溢出。
  4. 缓存利用:频繁查询最大排序号或检查名称是否存在时,可引入缓存层减少数据库压力。

目前的 EfCoreMyEntityNameRepository 实现了基本的查询优化,如使用 Specification 模式和动态排序,但未提供批量操作的专用接口。

本节来源

调用示例与异常处理

调用示例

// 批量删除
var idsToDelete = new List<Guid> { id1, id2, id3 };
await myEntityNameAppService.DeleteManyAsync(idsToDelete);

// 批量克隆
var idsToClone = new List<Guid> { id1, id2 };
var clonedEntities = await myEntityNameAppService.CloneAsync(idsToClone);

异常处理

  • 名称冲突CloneAsync 内部通过循环检查名称唯一性,避免 NameAlreadyExists 异常。
  • 输入验证CheckCreateOrUpdateDtoAsync 方法确保输入数据的有效性,防止空值或超长字符串。
  • 并发冲突:实体更新时通过 SetConcurrencyStampIfNotNull 支持乐观锁,防止并发修改覆盖。

建议在调用端捕获 UserFriendlyException 并向用户展示友好提示。

本节来源

高并发环境下的挑战与解决方案

挑战

  1. 排序冲突:多个用户同时调整排序可能导致 AdjustSortAsync 计算出错。
  2. 名称竞争:高并发克隆时,名称唯一性检查可能存在时间窗口,导致重复名称。
  3. 性能瓶颈:循环删除/插入在高负载下响应缓慢。

解决方案

  1. 分布式锁:对排序调整等敏感操作加锁,确保串行执行。
  2. 数据库约束:在数据库层面添加唯一索引,作为最后一道防线防止名称重复。
  3. 异步队列:将批量操作放入后台任务队列(如 Hangfire),避免阻塞主线程。
  4. 乐观锁增强:在 AdjustSortAsync 中加入版本号检查,提高并发安全性。

本节来源

结论

MyEntityNameAppService 中的批量删除与克隆功能实现了基本的业务需求,利用 ABP 框架的仓储模式和审计功能确保了数据一致性和可追溯性。克隆逻辑合理,能正确处理唯一标识和审计字段的重置。然而,当前的批量删除为逐条执行,存在性能瓶颈。建议在高并发场景下引入批量数据库操作、并行处理和分布式锁机制以提升性能和数据安全性。未来可考虑将 DeleteManyAsync 优化为真正的批量操作,并为 CloneAsync 提供事务性保证。