导入导出
本文档中引用的文件
/lmes-template/src/CMS.Plugin.MyPluginName.Application.Contracts/Dtos/MyEntityNames/MyEntityNamesExportModel.cs/lmes-template/src/CMS.Plugin.MyPluginName.Application.Contracts/Dtos/MyEntityNames/MyEntityNamesImportModel.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/MyEntityNames/MyEntityName.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/MyEntityNames/MyEntityNameSpecification.cs/lmes-template/src/CMS.Plugin.MyPluginName/Controller/MyEntityNameController.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/MyEntityNames/IMyEntityNameRepository.cs
目录
简介
本文档全面讲解系统中Excel导入导出功能的实现细节,重点围绕MyEntityNamesExportModel和MyEntityNamesImportModel两个核心数据传输模型展开。文档将详细说明这些模型如何与领域实体MyEntityName进行数据转换,解析导出功能如何利用MiniExcelLibs生成带格式的Excel模板(包含表头、数据验证规则和示例数据),并深入剖析导入流程中的文件解析、数据校验、错误收集与反馈机制。同时,文档还将重点阐述在处理大数据量导入时的内存优化策略,如流式读取与分批插入,为开发者提供完整的实现参考。
模型结构设计
本节分析MyEntityNamesExportModel、MyEntityNamesImportModel与领域实体MyEntityName之间的结构设计与转换关系。
MyEntityNamesExportModel 结构
MyEntityNamesExportModel是用于导出功能的数据模型,其内部定义了嵌套类WorkSectionExportModel,用于映射Excel中的每一行数据。该模型通过MiniExcelLibs.Attributes命名空间下的特性来定义Excel列的元数据。
- ExcelColumn特性:使用
[ExcelColumn]特性标注属性,指定列名(Name)和宽度(Width),确保导出的Excel文件具有清晰的中文表头和合适的列宽。 - 属性映射:
Name、Code、Remark三个属性分别对应“名称”、“编号”、“备注”三列,与领域实体MyEntityName的属性保持一致。
Section sources
/lmes-template/src/CMS.Plugin.MyPluginName.Application.Contracts/Dtos/MyEntityNames/MyEntityNamesExportModel.cs
MyEntityNamesImportModel 结构
MyEntityNamesImportModel是用于导入功能的数据模型,其设计巧妙地复用了导出模型的结构。
- 继承关系:
MyEntityNameImportModel类继承自WorkSectionExportModel,实现了模型复用,保证了导入模板的列结构与导出模板完全一致。 - 行号追踪:新增
RowIndex属性,用于记录数据在Excel中的行号(从第2行开始),便于在数据校验失败时精确定位错误位置,提供友好的错误反馈。 - 集合属性:
MyEntityNames属性是一个List<MyEntityNameImportModel>,在设置值时会自动为每一条记录分配行号,简化了行号管理逻辑。
Section sources
/lmes-template/src/CMS.Plugin.MyPluginName.Application.Contracts/Dtos/MyEntityNames/MyEntityNamesImportModel.cs
领域实体 MyEntityName
MyEntityName是位于领域层的核心实体,代表了业务中的一个聚合根。
- 属性定义:包含
Code(编号)、Name(名称)、Sort(排序)、Remark(备注)、IsDisabled(是否禁用)等核心业务属性。 - 构造函数与验证:在构造函数和
Update方法中,使用Volo.Abp.Check工具类对输入参数进行非空和长度校验,确保了领域模型的完整性。 - 行为方法:提供了
Update和AdjustSort等方法来封装业务逻辑,符合领域驱动设计(DDD)的原则。
Section sources
/lmes-template/src/CMS.Plugin.MyPluginName.Domain/MyEntityNames/MyEntityName.cs
模型转换关系
Diagram sources
/lmes-template/src/CMS.Plugin.MyPluginName.Application.Contracts/Dtos/MyEntityNames/MyEntityNamesExportModel.cs/lmes-template/src/CMS.Plugin.MyPluginName.Application.Contracts/Dtos/MyEntityNames/MyEntityNamesImportModel.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/MyEntityNames/MyEntityName.cs
导出功能实现
导出功能通过MyEntityNameController中的ExportAsync方法实现,利用MiniExcelLibs库将领域数据填充到预定义的Excel模板中。
实现流程
- 数据准备:调用
_myEntityNameAppService.ExportAsync(input)获取需要导出的数据。input.MaxResultCount被设置为int.MaxValue,确保获取所有数据。 - 模板定位:程序首先在应用根目录的
Resources/Templates/路径下查找名为MyEntityName导出模板.xlsx的模板文件;若未找到,则尝试在程序集所在目录下查找。 - 模板填充:使用
memoryStream.SaveAsByTemplateAsync(templatePath, exportData.Sheets)方法,将获取到的数据(exportData.Sheets)填充到模板的对应位置。 - 文件返回:将生成的Excel文件流以
FileStreamResult的形式返回给客户端,并设置下载文件名(包含时间戳以避免重复)。
模板设计
导出模板(MyEntityName导出模板.xlsx)是一个预先设计好的Excel文件,它包含了:
- 格式化表头:如“名称”、“编号”、“备注”等,与
WorkSectionExportModel的ExcelColumn特性定义一致。 - 数据验证规则:可能包含对“编号”列的唯一性约束或对“名称”列的长度限制。
- 示例数据:模板中可能包含一行示例数据,指导用户如何正确填写。
Diagram sources
/lmes-template/src/CMS.Plugin.MyPluginName/Controller/MyEntityNameController.cs
Section sources
/lmes-template/src/CMS.Plugin.MyPluginName/Controller/MyEntityNameController.cs
导入流程解析
导入功能通过MyEntityNameController中的ImportAsync方法实现,其流程包括文件解析、数据校验和持久化。
实现流程
- 文件接收与读取:接收
IFormFile file,将其内容复制到MemoryStream中以便后续处理。 - 工作表识别:使用
stream.GetSheetNames()获取所有工作表名称,并检查是否存在名为“配置”的工作表。 - 数据解析:如果存在“配置”工作表,则使用
MiniExcel.Query<MyEntityNamesImportModel.MyEntityNameImportModel>(stream, sheetName: "配置")将Excel数据直接映射为MyEntityNameImportModel对象列表。此过程利用了模型上的ExcelColumn特性进行列名匹配。 - 空数据检查:如果解析出的数据为空,则抛出
UserFriendlyException异常,提示用户检查表格。 - 业务处理:将解析出的数据封装成
MyEntityNamesImportModel对象,并调用_myEntityNameAppService.ImportAsync(...)进行后续的业务校验和数据持久化。
数据校验机制
数据校验主要在应用服务层(MyEntityNameAppService)实现,可能使用了MyEntityNameSpecification等规约模式。
- MyEntityNameSpecification:该规约类可用于构建查询表达式,例如根据名称查询实体。在导入时,可以利用此类检查“名称”是否已存在,以防止数据重复。
- 错误收集:校验过程会收集所有错误(如必填项为空、长度超限、唯一性冲突等),并将这些错误与对应的
RowIndex关联起来。 - 反馈机制:校验失败后,服务会返回一个包含所有错误详情(如“第3行:名称不能为空”)的结果对象,前端可以据此展示详细的错误报告。
Diagram sources
/lmes-template/src/CMS.Plugin.MyPluginName/Controller/MyEntityNameController.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/MyEntityNames/MyEntityNameSpecification.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/MyEntityNames/IMyEntityNameRepository.cs
Section sources
/lmes-template/src/CMS.Plugin.MyPluginName/Controller/MyEntityNameController.cs
大数据量导入优化策略
为应对大数据量导入可能引发的内存溢出问题,系统采用了以下优化策略:
流式读取
虽然当前代码示例中使用了MemoryStream,但在处理超大文件时,应采用MiniExcel提供的流式API(如QueryAsync配合IAsyncEnumerable),逐行读取和处理数据,而不是一次性将所有数据加载到内存中。这可以显著降低内存峰值。
分批插入
在应用服务的ImportAsync方法中,不应一次性调用仓储的InsertManyAsync。正确的做法是:
- 将导入的数据列表分割成较小的批次(例如每批1000条)。
- 遍历每个批次,调用仓储的批量插入方法。
- 在批次之间可以进行显式的
SaveChanges或利用事务,确保数据一致性的同时,避免单次数据库操作过大。
这种分批处理策略可以有效控制数据库连接的负载和内存使用,提高导入的稳定性和性能。
Section sources
/lmes-template/src/CMS.Plugin.MyPluginName/Controller/MyEntityNameController.cs
导入模板设计规范
为确保导入功能的顺利执行,导入模板的设计应遵循以下规范:
- 工作表名称:必须包含一个名为“配置”的工作表,这是代码中硬编码的查找名称。
- 列名一致性:各列的标题必须与
WorkSectionExportModel中ExcelColumn特性的Name属性完全一致,即“名称”、“编号”、“备注”。 - 数据格式:
- “编号”和“名称”为必填项,且长度不能超过系统定义的最大值(由
MyEntityNameConsts定义)。 - “备注”为可选项。
- “编号”和“名称”为必填项,且长度不能超过系统定义的最大值(由
- 示例数据:建议在模板中提供1-2行示例数据,帮助用户理解数据格式。
- 避免合并单元格:数据区域应避免使用合并单元格,以免影响解析。
常见错误代码说明
| 错误代码/信息 | 原因 | 解决方案 |
|---|---|---|
请检查导入的表格 | 上传的文件中不存在“配置”工作表,或该工作表内无任何数据。 | 确认文件中包含名为“配置”的工作表,并确保数据从第2行开始填写。 |
名称不能为空 / 编号不能为空 | 某行数据的“名称”或“编号”列为空。 | 检查报错行号,确保必填项已填写。 |
名称已存在 | 某行数据的“名称”与数据库中已有记录重复。 | 修改名称以确保唯一性,或先删除/禁用旧记录。 |
编号已存在 | 某行数据的“编号”与数据库中已有记录重复。 | 修改编号以确保唯一性。 |
名称长度不能超过XX字符 | “名称”列的内容超过了系统设定的最大长度。 | 缩短名称长度。 |
编号长度不能超过XX字符 | “编号”列的内容超过了系统设定的最大长度。 | 缩短编号长度。 |
总结
Excel 导入导出使用 MyEntityNamesExportModel 和 MyEntityNamesImportModel 转换数据,并通过 MiniExcelLibs 生成、解析文件。导入时按规约校验数据,通过行号定位错误;大数据量可采用流式读取和分批插入。