批量操作
本文档中引用的文件
- MyEntityNameAppService.cs
- IMyEntityNameAppService.cs
- IMyEntityNameRepository.cs
- MyEntityName.cs
- EfCoreMyEntityNameRepository.cs
目录
简介
本文档详细说明了在 MyEntityNameAppService 中实现的批量删除与克隆功能的技术细节。重点分析 DeleteBatchAsync 和 CloneAsync 方法的内部逻辑,包括如何通过仓储接口 IMyEntityNameRepository 批量获取实体、处理事务一致性以及记录操作日志。同时探讨克隆过程中核心属性的复制机制、新唯一标识的生成方式,以及审计字段(如创建人、创建时间)的正确重置。此外,还讨论了批量操作的性能优化策略,如分批处理、异步执行和数据库批量支持,并提供调用示例和异常处理建议。
核心组件分析
本节分析实现批量操作的核心类和接口。
图示来源
本节来源
批量删除功能实现
DeleteManyAsync 方法实现了对多个实体的批量删除功能。该方法接收一个 IEnumerable<Guid> 类型的 ID 集合,遍历每个 ID 并调用 DeleteAsync 方法进行删除。
该方法虽然名为“批量”,但实际上是通过循环逐个删除实体,而非使用数据库层面的批量删除操作。这意味着每次删除都会触发一次数据库事务,可能影响性能。然而,这种方式能够更好地控制错误处理——如果某个删除失败,可以捕获异常并决定是否继续后续删除。
图示来源
本节来源
克隆功能实现
CloneAsync 方法实现了实体的克隆功能。其核心逻辑如下:
- 获取当前最大排序值
sort,用于为新克隆的实体分配连续的排序号。 - 遍历传入的 ID 列表,通过
FindAsync获取原始实体。 - 对每个实体,生成新的 名称:原始名称 + 克隆标记(由
MyEntityNameConsts.CloneTag定义),并检查新名称是否已存在,若存在则追加更多标记以确保唯一性。 - 调用实体的
Clone方法创建新实例,传入新的 GUID、新名称和递增的排序号。 - 通过
InsertAsync将新实体插入数据库。
克隆过程中,MyEntityName 类的 Clone 方法仅复制了核心业务属性(Code、Name、Remark、Sort),而审计字段(如 CreatorId、CreationTime)由基类 FullAuditedAggregateRoot<Guid> 在插入时自动重置为当前用户和当前时间,确保了审计信息的正确性。
图示来源
本节来源
事务一致性与操作日志
尽管 DeleteManyAsync 和 CloneAsync 方法本身未显式使用事务包装,但由于它们运行在 ABP 框架的上下文中,默认情况下每个仓储操作(如 InsertAsync、DeleteAsync)都会在独立的事务中执行,保证了单个操作的原子性。
对于 CloneAsync,虽然每个插入是独立事务,但方法内部通过顺序执行确保了逻辑上的连续性。若需保证所有克隆操作的完全原子性(即全部成功或全部失败),应考虑在应用服务层使用 IUnitOfWorkManager 显式开启一个跨多个操作的事务。
操作日志方面,ABP 框架的审计功能(通过继承 FullAuditedAggregateRoot<Guid>)自动记录了每个实体的创建人、创建时间、最后修改人和最后修改时间。这些信息在克隆时被正确重置,体现了框架对审计字段的良好支持。
本节来源
性能优化策略
当前实现存在以下性能优化空间:
- 批量数据库操作:
DeleteManyAsync使用循环删除,可优化为使用原生 SQL 或 EF Core 的ExecuteDeleteAsync方法进行真正的批量删除,减少数据库往返次数。 - 并行处理:对于非顺序依赖的操作(如独立实体的克隆),可考虑使用
Parallel.ForEachAsync或Task.WhenAll并行执行,提升吞吐量。 - 分批处理:当处理大量实体时,应实现分页或分批机制,避免一次性加载过多数据导致内存溢出。
- 缓存利用:频繁查询最大排序号或检查名称是否存在时,可引入缓存层减少数据库压力。
目前的 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 并向用户展示友好提示。
本节来源