使用 EPPlus 封装的 excel 表格导入功能 (二) delegate 委托 --永远滴神

前言

接上一篇 使用 EPPlus 封装的 excel 表格导入功能 (一)

前一篇的是大概能用但是不一定好用的版本

后来我又重新封装扩展了一下

支持自定义更多东西(但是封装地是否有必要我就说不清了)

上个版本的问题

上个版本封装之后的使用代码:

  1. public ICollection<TestDto> ExcelImport(IFormFile file)
  2. {
  3. var config = ExcelCellOption<TestDto>
  4. .GenExcelOption("姓名", item => item.Name)
  5. .Add("年龄", item => item.Age, item => int.Parse(item))
  6. .Add("性别", item => item.Gender, item => item == "男")
  7. .Add("身高", item => item.Height, item => double.Parse(item));
  8. ICollection<TestDto> result = ExcelOperation.ExcelToEntity(file.OpenReadStream(), config);
  9. return result;
  10. }

设计逻辑是 一个单元格 对应 实体中的一个字段

这个设计一开始是没什么问题的,将excel中的数据导入到 DTO集合 中,然后就可以进行下一步的操作

但在实际使用的时候发现,拿到 DTO集合 之后,总是需要再套个 foreach 做一些初始化操作

比如:

  • 通过身份证号码判断性别
  • 通过用户名查询账单信息
  • 给某个字段赋值
  • 对应导入的数据生成其他的数据
  • ......

基于以上的需要,重新修改后的调用代码如下:

  1. public ICollection<TestDto> ExcelImport(IFormFile file)
  2. {
  3. var config = new ExcelImportOption<TestDto>()
  4. .Add("姓名", item => item.Name)
  5. .Add("年龄", item => item.Age, item => int.Parse(item))
  6. .Default(item => item.Height, 233)
  7. .AddInit(item =>
  8. {
  9. item.Name += "hhhhhhhh";
  10. item.Gender = false;
  11. return item;
  12. });
  13. ICollection<TestDto> result = ExcelOperation.ExcelToEntity(file.OpenReadStream(), config);
  14. return result;
  15. }

代码/设计/想法

由于我想在解析 excel 的同时进行初始化操作,所以委托这东西必不可少

  1. Func<T, T> Init { get; set; }

由于现阶段的需求比较简单,不需要什么复杂的参数,所以使用 Func<>Action<> 都可以

定义这个初始化 Init 之后,单纯的 ICollection<ExcelCellOption<T>> 已经是不够用了

所以重新定义了一个 配置

  1. public class ExcelOption<T>
  2. {
  3. public ICollection<ExcelCellOption<T>> FieldOption { get; set; } = new List<ExcelCellOption<T>>();
  4. public ICollection<ExcelCellOption<T>> DefaultOption { get; set; } = new List<ExcelCellOption<T>>();
  5. public Func<T, T> Init { get; set; }
  6. }

其中的 DefaultOption 没有也行

然后对 ExcelOption<T> 进行扩展


  1. public static class ExcelOptionExt
  2. {
  3. ......
  4. public static ExcelOption<T> AddInit<T>(this ExcelOption<T> origin, Func<T, T> action = null)
  5. {
  6. if (origin.Init == null) origin.Init = action;
  7. return origin;
  8. }
  9. // 没有 DefaultOption 的情况下使用 FieldOption 也行
  10. public static ExcelOption<T> Default<T, E>(this ExcelOption<T> origin, Expression<Func<T, E>> prop, object defaultValue = null)
  11. {
  12. var member = prop.GetMember();
  13. if (origin.DefaultOption == null) origin.DefaultOption = new List<ExcelCellOption<T>>();
  14. origin.DefaultOption.Add(new ExcelCellOption<T>
  15. {
  16. PropName = member.Name,
  17. Prop = (PropertyInfo)member,
  18. ExcelField = string.Empty,
  19. Action = item => defaultValue
  20. });
  21. return origin;
  22. }
  23. ......
  24. }

配置 接口改变之后,ExcelOperationExcelToEntity 方法也要进行修改


  1. public static ICollection<T> ExcelToEntity<T>(Stream excelStream, ExcelOption<T> options)
  2. {
  3. using ExcelPackage pack = new ExcelPackage(excelStream);
  4. // 合并 FieldOption 和 DefaultOption
  5. var propOptions = options.FieldOption.Concat(options.DefaultOption);
  6. var sheet = pack.Workbook.Worksheets[1];
  7. int rowCount = sheet.Dimension.Rows, colCount = sheet.Dimension.Columns;
  8. // 获取对应设置的 表头 以及其 column
  9. var header = sheet.Cells[1, 1, 1, sheet.Dimension.Columns]
  10. .Where(item => propOptions.Any(opt => opt.ExcelField == item.Value?.ToString().Trim()))
  11. .ToDictionary(item => item.Value?.ToString().Trim(), item => item.End.Column);
  12. List<T> data = new List<T>();
  13. // 将excel 的数据转换为 对应实体
  14. for (int r = 2; r <= rowCount; r++)
  15. {
  16. // 将单行数据转换为 表头:数据 的键值对
  17. var rowData = sheet.Cells[r, 1, r, colCount]
  18. .Where(item => header.Any(title => title.Value == item.End.Column))
  19. .Select(item => new KeyValuePair<string, string>(header.First(title => title.Value == item.End.Column).Key, item.Value?.ToString().Trim()))
  20. .ToDictionary(item => item.Key, item => item.Value);
  21. var obj = Activator.CreateInstance(typeof(T));
  22. // 根据对应传入的设置 为obj赋值
  23. foreach (var option in propOptions)
  24. {
  25. if (!string.IsNullOrEmpty(option.ExcelField))
  26. {
  27. var value = rowData.ContainsKey(option.ExcelField) ? rowData[option.ExcelField] : string.Empty;
  28. if (!string.IsNullOrEmpty(value))
  29. option.Prop.SetValue(obj, option.Action == null ? value : option.Action(value));
  30. }
  31. else
  32. option.Prop.SetValue(obj, option.Action == null ? null : option.Action(string.Empty));
  33. }
  34. // 实际上只是加了个 Init 操作
  35. if (options.Init != null)
  36. obj = options.Init((T)obj);
  37. data.Add((T)obj);
  38. }
  39. return data;
  40. }

然后这个阶段就算是大功告成了!

再贴一次使用时的代码

  1. public ICollection<TestDto> ExcelImport(IFormFile file)
  2. {
  3. var config = new ExcelImportOption<TestDto>()
  4. .Add("姓名", item => item.Name)
  5. .Add("年龄", item => item.Age, item => int.Parse(item))
  6. .Default(item => item.Height, 233)
  7. .AddInit(item =>
  8. {
  9. item.Name += "hhhhhhhh";
  10. item.Gender = false;
  11. return item;
  12. });
  13. ICollection<TestDto> result = ExcelOperation.ExcelToEntity(file.OpenReadStream(), config);
  14. return result;
  15. }

完整代码


  1. public class ExcelOperation
  2. {
  3. public static ICollection<T> ExcelToEntity<T>(Stream excelStream, ExcelOption<T> options)
  4. {
  5. using ExcelPackage pack = new ExcelPackage(excelStream);
  6. // 合并 FieldOption 和 DefaultOption
  7. var propOptions = options.FieldOption.Concat(options.DefaultOption);
  8. var sheet = pack.Workbook.Worksheets[1];
  9. int rowCount = sheet.Dimension.Rows, colCount = sheet.Dimension.Columns;
  10. // 获取对应设置的 表头 以及其 column
  11. var header = sheet.Cells[1, 1, 1, sheet.Dimension.Columns]
  12. .Where(item => propOptions.Any(opt => opt.ExcelField == item.Value?.ToString().Trim()))
  13. .ToDictionary(item => item.Value?.ToString().Trim(), item => item.End.Column);
  14. List<T> data = new List<T>();
  15. // 将excel 的数据转换为 对应实体
  16. for (int r = 2; r <= rowCount; r++)
  17. {
  18. // 将单行数据转换为 表头:数据 的键值对
  19. var rowData = sheet.Cells[r, 1, r, colCount]
  20. .Where(item => header.Any(title => title.Value == item.End.Column))
  21. .Select(item => new KeyValuePair<string, string>(header.First(title => title.Value == item.End.Column).Key, item.Value?.ToString().Trim()))
  22. .ToDictionary(item => item.Key, item => item.Value);
  23. var obj = Activator.CreateInstance(typeof(T));
  24. // 根据对应传入的设置 为obj赋值
  25. foreach (var option in propOptions)
  26. {
  27. if (!string.IsNullOrEmpty(option.ExcelField))
  28. {
  29. var value = rowData.ContainsKey(option.ExcelField) ? rowData[option.ExcelField] : string.Empty;
  30. if (!string.IsNullOrEmpty(value))
  31. option.Prop.SetValue(obj, option.Action == null ? value : option.Action(value));
  32. }
  33. else
  34. option.Prop.SetValue(obj, option.Action == null ? null : option.Action(string.Empty));
  35. }
  36. if (options.Init != null)
  37. obj = options.Init((T)obj);
  38. data.Add((T)obj);
  39. }
  40. return data;
  41. }
  42. /// <summary>
  43. /// 将表格数据转换为指定的数据实体
  44. /// </summary>
  45. public static ICollection<T> ExcelToEntity<T>(Stream excelStream, ICollection<ExcelCellOption<T>> options)
  46. {
  47. using ExcelPackage pack = new ExcelPackage(excelStream);
  48. var sheet = pack.Workbook.Worksheets[1];
  49. int rowCount = sheet.Dimension.Rows, colCount = sheet.Dimension.Columns;
  50. // 获取对应设置的 表头 以及其 column
  51. var header = sheet.Cells[1, 1, 1, sheet.Dimension.Columns]
  52. .Where(item => options.Any(opt => opt.ExcelField == item.Value?.ToString().Trim()))
  53. .ToDictionary(item => item.Value?.ToString().Trim(), item => item.End.Column);
  54. List<T> data = new List<T>();
  55. // 将excel 的数据转换为 对应实体
  56. for (int r = 2; r <= rowCount; r++)
  57. {
  58. // 将单行数据转换为 表头:数据 的键值对
  59. var rowData = sheet.Cells[r, 1, r, colCount]
  60. .Where(item => header.Any(title => title.Value == item.End.Column))
  61. .Select(item => new KeyValuePair<string, string>(header.First(title => title.Value == item.End.Column).Key, item.Value?.ToString().Trim()))
  62. .ToDictionary(item => item.Key, item => item.Value);
  63. var obj = Activator.CreateInstance(typeof(T));
  64. // 根据对应传入的设置 为obj赋值
  65. foreach (var option in options)
  66. {
  67. if (!string.IsNullOrEmpty(option.ExcelField))
  68. {
  69. var value = rowData.ContainsKey(option.ExcelField) ? rowData[option.ExcelField] : string.Empty;
  70. if (!string.IsNullOrEmpty(value))
  71. option.Prop.SetValue(obj, option.Action == null ? value : option.Action(value));
  72. }
  73. else
  74. option.Prop.SetValue(obj, option.Action == null ? null : option.Action(string.Empty));
  75. }
  76. data.Add((T)obj);
  77. }
  78. return data;
  79. }
  80. }
  81. public class ExcelOption<T>
  82. {
  83. public ICollection<ExcelCellOption<T>> FieldOption { get; set; } = new List<ExcelCellOption<T>>();
  84. public ICollection<ExcelCellOption<T>> DefaultOption { get; set; } = new List<ExcelCellOption<T>>();
  85. public Func<T, T> Init { get; set; }
  86. }
  87. public class ExcelCellOption<T>
  88. {
  89. /// <summary>
  90. /// 对应excel中的header字段
  91. /// </summary>
  92. public string ExcelField { get; set; }
  93. /// <summary>
  94. /// 对应字段的属性(实际上包含PropName)
  95. /// </summary>
  96. public PropertyInfo Prop { get; set; }
  97. /// <summary>
  98. /// 就是一个看起来比较方便的标识
  99. /// </summary>
  100. public string PropName { get; set; }
  101. /// <summary>
  102. /// 转换 表格 数据的方法 (现在是个鸡肋了)
  103. /// </summary>
  104. public Func<string, object> Action { get; set; }
  105. public static ICollection<ExcelCellOption<T>> GenExcelOption<E>(string field, Expression<Func<T, E>> prop, Func<string, object> action = null)
  106. {
  107. var member = prop.GetMember();
  108. return new List<ExcelCellOption<T>>{
  109. new ExcelCellOption<T>
  110. {
  111. PropName = member.Name,
  112. Prop = (PropertyInfo)member,
  113. ExcelField = field,
  114. Action = action
  115. }
  116. };
  117. }
  118. }
  119. public static class ExcelOptionExt
  120. {
  121. public static ICollection<ExcelCellOption<T>> Add<T, E>(this ICollection<ExcelCellOption<T>> origin, string field, Expression<Func<T, E>> prop, Func<string, object> action = null)
  122. {
  123. var member = prop.GetMember();
  124. origin.Add(new ExcelCellOption<T>
  125. {
  126. PropName = member.Name,
  127. Prop = (PropertyInfo)member,
  128. ExcelField = field,
  129. Action = action
  130. });
  131. return origin;
  132. }
  133. public static ExcelOption<T> Add<T, E>(this ExcelOption<T> origin, string field, Expression<Func<T, E>> prop, Func<string, object> action = null)
  134. {
  135. var member = prop.GetMember();
  136. if (origin.FieldOption == null) origin.FieldOption = new List<ExcelCellOption<T>>();
  137. origin.FieldOption.Add(new ExcelCellOption<T>
  138. {
  139. PropName = member.Name,
  140. Prop = (PropertyInfo)member,
  141. ExcelField = field,
  142. Action = action
  143. });
  144. return origin;
  145. }
  146. public static ExcelOption<T> Default<T, E>(this ExcelOption<T> origin, Expression<Func<T, E>> prop, object defaultValue = null)
  147. {
  148. var member = prop.GetMember();
  149. if (origin.DefaultOption == null) origin.DefaultOption = new List<ExcelCellOption<T>>();
  150. origin.DefaultOption.Add(new ExcelCellOption<T>
  151. {
  152. PropName = member.Name,
  153. Prop = (PropertyInfo)member,
  154. ExcelField = string.Empty,
  155. Action = item => defaultValue
  156. });
  157. return origin;
  158. }
  159. public static ExcelOption<T> AddInit<T>(this ExcelOption<T> origin, Func<T, T> action = null)
  160. {
  161. if (origin.Init == null) origin.Init = action;
  162. return origin;
  163. }
  164. }

未完待续

使用 EPPlus 封装的 excel 表格导入功能 (二) delegate 委托 --永远滴神的更多相关文章

  1. 使用 EPPlus 封装的 excel 表格导入功能 (.net core c#)

    使用 EPPlus 封装的 excel 表格导入功能 前言 最近做系统的时候有很多 excel导入 的功能,以前我前后端都做的时候是在前端解析,然后再做个批量插入的接口 我觉着这样挺好的,后端部分可以 ...

  2. WEB下的excel批量导入功能

    新手学习中,记录一下excel导出功能实现的整个流程.使用框架ssm. control层 importExcel+parseDataItem: @RequestMapping("import ...

  3. java实现excel表格导入数据库表

    导入excel就是一个上传excel文件,然后获取excel文件数据,然后处理数据并插入到数据库的过程 一.上传excel 前端jsp页面,我的是index.jsp 在页面中我自己加入了一个下载上传文 ...

  4. 从Excel表格导入数据到数据库

    数据库:SQL 1.小数据直接粘贴 2.用导入向导 3.用SSIS包 4.用SQL语句 现在详细说一下第4种方法,以.xlsx文件为例 .xlsx文件需要用provider“Microsoft.ACE ...

  5. Excel表格导入Mysql数据库,一行存入多条数据的前后台完整实现思路(使用mybatis框架)

    现在有一张Excel表格: 存入数据库时需要这样存放: 现在需要将Excel表格做处理,将每一行拆分成多条数据存入数据库. 1.首先在前台jsp页面画一个按钮:,加入点击事件: <td styl ...

  6. 使用Excel表格导入数据到Oracle表

    在工作中我们会遇到将通过数据手动录入到系统中的需求,如果数据量比较小,那么手动输入是可行的,倘若数据量很大,那么这些数据手动录入将会是一个很大的工作量,为了简化这个手动录入的操作流程,我们可以使用Ex ...

  7. 前端Excel表格导入导出,包括合并单元格,表格自定义样式等

    表格数据导入 读取导入Excel表格数据这里采用的是 xlsx 插件 npm i xlsx 读取excel需要通过 XLSX.read(data, {type: type}) 方法来实现,返回一个叫W ...

  8. C#解决从含身份证号码的Excel表格导入数据库的问题

    用C#做从Excel表导入SQL数据库时发现从EXCEL导入的身份证号码会变成科学表示方法. 解决这个问题是比较容易的,首先,打开电子表格,选中“身份证号码”一列,右键选择“设置单元格格式”,进入单元 ...

  9. Python脚本:实现excel表格导入到数据库,支持mysql,postgresql,MongoDB

    import xlrd,re from datetime import datetime from xlrd import xldate_as_tuple # 判断上传表格是否与模板要求一致 def ...

随机推荐

  1. 2021 年学习 React 的所需要的 JavaScript 基础

    在理想的情况中,您可以先了解所有有关 JavaScript 和 web 开发的知识,然后再深入了解React. 但是,我们没有办法这样,如果等你把所有 JavaScript 的知识都掌握了再去学习 R ...

  2. [C#] (原创)一步一步教你自定义控件——06,MaskLayer(遮罩层)

    一.前言 技术没有先进与落后,只有合适与不合适. 本篇的自定义控件是:遮罩层(MaskLayer). 遮罩层对软件的美观与易用性上的提高是很大的,在日常使用过程中也会经常看到各种遮罩层,虽然WinFo ...

  3. ElasticSearcher的安装以及安装过程中出现的问题

    先给出参考链接,带安装成功后再进行总结整个过程. 参考链接:https://blog.csdn.net/fjyab/article/details/81101284 java操作ElasticSear ...

  4. vue:表单验证时,trigger的值什么时候选blur什么时候选change

    对el-input输入框的验证,trigger的值选blur,即失去焦点时进行验证. 下拉框(el-select).日期选择器(el-date-picker).复选框(el-checkbox).单选框 ...

  5. CNN结构演变总结(一)经典模型

    导言:    自2012年AlexNet在ImageNet比赛上获得冠军,卷积神经网络逐渐取代传统算法成为了处理计算机视觉任务的核心.    在这几年,研究人员从提升特征提取能力,改进回传梯度更新效果 ...

  6. EFCodeFirst属性映射约定

    EFCodeFirst属性映射约定 EFCodeFirst 属性映射约定 CodeFirst与数据表之间得映射方式又两种:Data Annotation和Fluent API 默认约定: 表名为类名的 ...

  7. 一些 html+css 细节

    一. input 光标(插入符)颜色 input: { caret-color: #c0c0ff; } 二. 修改 placeholder 颜色 input::placeholder { color: ...

  8. dubbo使用和配置讲解

    1. 分布式系统中相关概念 1.1 互联网项目特点及目标 1.1.1 特点: 用户多 流量大.并发高 海量数据 易受攻击 功能繁琐 变更快 1.1.2 指标及相关目标 互联网项目三高目标:高并发.高可 ...

  9. JAVA多线程与锁机制

    JAVA多线程与锁机制 1 关于Synchronized和lock synchronized是Java的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码 ...

  10. DRF(django rest-framework)

    1.什么是DRF django组件,快速帮助我们开发遵循restful规范的一个组件 2.什么是restful规范 RESTful的URL用于指定资源,URL中只能使用名词的组合来标识资源," ...