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

前言

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

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

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

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

上个版本的问题

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

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

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

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

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

比如:

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

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

public ICollection<TestDto> ExcelImport(IFormFile file)
{
var config = new ExcelImportOption<TestDto>()
.Add("姓名", item => item.Name)
.Add("年龄", item => item.Age, item => int.Parse(item))
.Default(item => item.Height, 233)
.AddInit(item =>
{
item.Name += "hhhhhhhh";
item.Gender = false;
return item;
});
ICollection<TestDto> result = ExcelOperation.ExcelToEntity(file.OpenReadStream(), config); return result;
}

代码/设计/想法

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

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

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

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

所以重新定义了一个 配置

public class ExcelOption<T>
{
public ICollection<ExcelCellOption<T>> FieldOption { get; set; } = new List<ExcelCellOption<T>>();
public ICollection<ExcelCellOption<T>> DefaultOption { get; set; } = new List<ExcelCellOption<T>>();
public Func<T, T> Init { get; set; }
}

其中的 DefaultOption 没有也行

然后对 ExcelOption<T> 进行扩展


public static class ExcelOptionExt
{ ...... public static ExcelOption<T> AddInit<T>(this ExcelOption<T> origin, Func<T, T> action = null)
{
if (origin.Init == null) origin.Init = action;
return origin;
}
// 没有 DefaultOption 的情况下使用 FieldOption 也行
public static ExcelOption<T> Default<T, E>(this ExcelOption<T> origin, Expression<Func<T, E>> prop, object defaultValue = null)
{
var member = prop.GetMember();
if (origin.DefaultOption == null) origin.DefaultOption = new List<ExcelCellOption<T>>();
origin.DefaultOption.Add(new ExcelCellOption<T>
{
PropName = member.Name,
Prop = (PropertyInfo)member,
ExcelField = string.Empty,
Action = item => defaultValue
});
return origin;
} ......
}

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


public static ICollection<T> ExcelToEntity<T>(Stream excelStream, ExcelOption<T> options)
{
using ExcelPackage pack = new ExcelPackage(excelStream);
// 合并 FieldOption 和 DefaultOption
var propOptions = options.FieldOption.Concat(options.DefaultOption); var sheet = pack.Workbook.Worksheets[1];
int rowCount = sheet.Dimension.Rows, colCount = sheet.Dimension.Columns;
// 获取对应设置的 表头 以及其 column
var header = sheet.Cells[1, 1, 1, sheet.Dimension.Columns]
.Where(item => propOptions.Any(opt => opt.ExcelField == item.Value?.ToString().Trim()))
.ToDictionary(item => item.Value?.ToString().Trim(), item => item.End.Column);
List<T> data = new List<T>();
// 将excel 的数据转换为 对应实体
for (int r = 2; r <= rowCount; r++)
{
// 将单行数据转换为 表头:数据 的键值对
var rowData = sheet.Cells[r, 1, r, colCount]
.Where(item => header.Any(title => title.Value == item.End.Column))
.Select(item => new KeyValuePair<string, string>(header.First(title => title.Value == item.End.Column).Key, item.Value?.ToString().Trim()))
.ToDictionary(item => item.Key, item => item.Value);
var obj = Activator.CreateInstance(typeof(T));
// 根据对应传入的设置 为obj赋值
foreach (var option in propOptions)
{
if (!string.IsNullOrEmpty(option.ExcelField))
{
var value = rowData.ContainsKey(option.ExcelField) ? rowData[option.ExcelField] : string.Empty;
if (!string.IsNullOrEmpty(value))
option.Prop.SetValue(obj, option.Action == null ? value : option.Action(value));
}
else
option.Prop.SetValue(obj, option.Action == null ? null : option.Action(string.Empty));
}
// 实际上只是加了个 Init 操作
if (options.Init != null)
obj = options.Init((T)obj);
data.Add((T)obj);
}
return data;
}

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

再贴一次使用时的代码

public ICollection<TestDto> ExcelImport(IFormFile file)
{
var config = new ExcelImportOption<TestDto>()
.Add("姓名", item => item.Name)
.Add("年龄", item => item.Age, item => int.Parse(item))
.Default(item => item.Height, 233)
.AddInit(item =>
{
item.Name += "hhhhhhhh";
item.Gender = false;
return item;
});
ICollection<TestDto> result = ExcelOperation.ExcelToEntity(file.OpenReadStream(), config); return result;
}

完整代码


public class ExcelOperation
{ public static ICollection<T> ExcelToEntity<T>(Stream excelStream, ExcelOption<T> options)
{
using ExcelPackage pack = new ExcelPackage(excelStream);
// 合并 FieldOption 和 DefaultOption
var propOptions = options.FieldOption.Concat(options.DefaultOption); var sheet = pack.Workbook.Worksheets[1];
int rowCount = sheet.Dimension.Rows, colCount = sheet.Dimension.Columns;
// 获取对应设置的 表头 以及其 column
var header = sheet.Cells[1, 1, 1, sheet.Dimension.Columns]
.Where(item => propOptions.Any(opt => opt.ExcelField == item.Value?.ToString().Trim()))
.ToDictionary(item => item.Value?.ToString().Trim(), item => item.End.Column);
List<T> data = new List<T>();
// 将excel 的数据转换为 对应实体
for (int r = 2; r <= rowCount; r++)
{
// 将单行数据转换为 表头:数据 的键值对
var rowData = sheet.Cells[r, 1, r, colCount]
.Where(item => header.Any(title => title.Value == item.End.Column))
.Select(item => new KeyValuePair<string, string>(header.First(title => title.Value == item.End.Column).Key, item.Value?.ToString().Trim()))
.ToDictionary(item => item.Key, item => item.Value);
var obj = Activator.CreateInstance(typeof(T));
// 根据对应传入的设置 为obj赋值
foreach (var option in propOptions)
{
if (!string.IsNullOrEmpty(option.ExcelField))
{
var value = rowData.ContainsKey(option.ExcelField) ? rowData[option.ExcelField] : string.Empty;
if (!string.IsNullOrEmpty(value))
option.Prop.SetValue(obj, option.Action == null ? value : option.Action(value));
}
else
option.Prop.SetValue(obj, option.Action == null ? null : option.Action(string.Empty));
}
if (options.Init != null)
obj = options.Init((T)obj);
data.Add((T)obj);
}
return data;
} /// <summary>
/// 将表格数据转换为指定的数据实体
/// </summary>
public static ICollection<T> ExcelToEntity<T>(Stream excelStream, ICollection<ExcelCellOption<T>> options)
{
using ExcelPackage pack = new ExcelPackage(excelStream);
var sheet = pack.Workbook.Worksheets[1];
int rowCount = sheet.Dimension.Rows, colCount = sheet.Dimension.Columns;
// 获取对应设置的 表头 以及其 column
var header = sheet.Cells[1, 1, 1, sheet.Dimension.Columns]
.Where(item => options.Any(opt => opt.ExcelField == item.Value?.ToString().Trim()))
.ToDictionary(item => item.Value?.ToString().Trim(), item => item.End.Column);
List<T> data = new List<T>();
// 将excel 的数据转换为 对应实体
for (int r = 2; r <= rowCount; r++)
{
// 将单行数据转换为 表头:数据 的键值对
var rowData = sheet.Cells[r, 1, r, colCount]
.Where(item => header.Any(title => title.Value == item.End.Column))
.Select(item => new KeyValuePair<string, string>(header.First(title => title.Value == item.End.Column).Key, item.Value?.ToString().Trim()))
.ToDictionary(item => item.Key, item => item.Value);
var obj = Activator.CreateInstance(typeof(T));
// 根据对应传入的设置 为obj赋值
foreach (var option in options)
{
if (!string.IsNullOrEmpty(option.ExcelField))
{
var value = rowData.ContainsKey(option.ExcelField) ? rowData[option.ExcelField] : string.Empty;
if (!string.IsNullOrEmpty(value))
option.Prop.SetValue(obj, option.Action == null ? value : option.Action(value));
}
else
option.Prop.SetValue(obj, option.Action == null ? null : option.Action(string.Empty));
}
data.Add((T)obj); }
return data;
}
} public class ExcelOption<T>
{
public ICollection<ExcelCellOption<T>> FieldOption { get; set; } = new List<ExcelCellOption<T>>();
public ICollection<ExcelCellOption<T>> DefaultOption { get; set; } = new List<ExcelCellOption<T>>();
public Func<T, T> Init { get; set; }
} public class ExcelCellOption<T>
{
/// <summary>
/// 对应excel中的header字段
/// </summary>
public string ExcelField { get; set; }
/// <summary>
/// 对应字段的属性(实际上包含PropName)
/// </summary>
public PropertyInfo Prop { get; set; }
/// <summary>
/// 就是一个看起来比较方便的标识
/// </summary>
public string PropName { get; set; }
/// <summary>
/// 转换 表格 数据的方法 (现在是个鸡肋了)
/// </summary>
public Func<string, object> Action { get; set; }
public static ICollection<ExcelCellOption<T>> GenExcelOption<E>(string field, Expression<Func<T, E>> prop, Func<string, object> action = null)
{
var member = prop.GetMember();
return new List<ExcelCellOption<T>>{
new ExcelCellOption<T>
{
PropName = member.Name,
Prop = (PropertyInfo)member,
ExcelField = field,
Action = action
}
};
}
} public static class ExcelOptionExt
{
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)
{
var member = prop.GetMember();
origin.Add(new ExcelCellOption<T>
{
PropName = member.Name,
Prop = (PropertyInfo)member,
ExcelField = field,
Action = action
});
return origin;
} public static ExcelOption<T> Add<T, E>(this ExcelOption<T> origin, string field, Expression<Func<T, E>> prop, Func<string, object> action = null)
{
var member = prop.GetMember();
if (origin.FieldOption == null) origin.FieldOption = new List<ExcelCellOption<T>>();
origin.FieldOption.Add(new ExcelCellOption<T>
{
PropName = member.Name,
Prop = (PropertyInfo)member,
ExcelField = field,
Action = action
});
return origin;
}
public static ExcelOption<T> Default<T, E>(this ExcelOption<T> origin, Expression<Func<T, E>> prop, object defaultValue = null)
{
var member = prop.GetMember();
if (origin.DefaultOption == null) origin.DefaultOption = new List<ExcelCellOption<T>>();
origin.DefaultOption.Add(new ExcelCellOption<T>
{
PropName = member.Name,
Prop = (PropertyInfo)member,
ExcelField = string.Empty,
Action = item => defaultValue
});
return origin;
}
public static ExcelOption<T> AddInit<T>(this ExcelOption<T> origin, Func<T, T> action = null)
{
if (origin.Init == null) origin.Init = action;
return origin;
}
}

未完待续

使用 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. django学习-11.开发一个简单的醉得意菜单和人均支付金额查询页面

    1.前言 刚好最近跟技术部门的[产品人员+UI人员+测试人员],组成了一桌可以去公司楼下醉得意餐厅吃饭的小team. 所以为了实现这些主要点餐功能: 提高每天中午点餐效率,把点餐时间由20分钟优化为1 ...

  2. ECMAScript 等性运算符

    判断两个变量是否相等是程序设计中非常重要的运算.在处理原始值时,这种运算相当简单,但涉及对象,任务就稍有点复杂. ECMAScript 提供了两套等性运算符:等号和非等号用于处理原始值,全等号和非全等 ...

  3. 1071 Speech Patterns——PAT甲级真题

    1071 Speech Patterns People often have a preference among synonyms of the same word. For example, so ...

  4. servlet内置对象(传递数据)

    一个servlet向另一个servlet发送数据,可以将数据放置在一个容器中(io.数据库.servlet的内置对象),servlet的内置对象成本最小. 一共有三个内置对象. 名字 类型 reque ...

  5. 痞子衡嵌入式:盘点国内RISC-V内核MCU厂商(2020年发布产品)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是国内RISC-V内核MCU厂商(2020). 虽然RISC-V风潮已经吹了好几年,但2019年才是其真正进入主流市场的元年,最近国内大量 ...

  6. 清除浏览器默认样式的reset.css(转载于reset.css的官方)

    /* http://meyerweb.com/eric/tools/css/reset/ v2.0-modified | 20110126 License: none (public domain) ...

  7. 构造方法和this的作用

    一.构造方法概述 构造方法是一个特殊的方法 是创建对象时候调用的方法 方法的名字很特殊:必须和类名保持一致,大小写都要一样 方法没有返回值 方法也没有返回值类型 构造方法无法在外部手动调用 publi ...

  8. linux 关闭对端口的监听

    netstat -anp | grep [端口号] [root@test-01 ~]# netstat -anp | grep 6665 tcp 0 0 0.0.0.0:6665 0.0.0.0:* ...

  9. 项目管理之Git

    @[TOC]( Git命令:分支与合并)Git一款很好的项目版本管理工具,更是一款优秀的分布式项目管理工具.今天主要给大家介绍Git 强大的分支和合并功能,分支和合并可以说在实际的工作当中用到的是最多 ...

  10. JAVA多线程与锁机制

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