乐于分享
好东西不私藏

一行代码导出 Excel —— 还带嵌套表头和中文标题

一行代码导出 Excel —— 还带嵌套表头和中文标题

导出 Excel 是每个业务系统的刚需。COM/VSTO 太重,EPPlus 要商业授权,手写 OpenXML 太繁琐……这个轻量级 Excel 映射器,不需要任何模板文件,你的 C# 对象就是模板


常见方案对比

方案依赖复杂度授权
COM InteropExcel 安装极高需 Office
EPPlus 5+.NET商业授权
OpenXML 裸写.NET极高MIT
NPOI.NETApache
ExcelMapperClosedXML极低MIT

基于 ClosedXML 封装,开源免费,API 简洁到极致。


30 秒上手

dotnet add package VassasCo.Utility.ExcelMapper
publicclassOrder{publicintId { getset; }publicstringCustomer { getset; }publicdecimalAmount { getset; }publicDateTimeOrderDate { getset; }}varorders=GetOrders();ExcelMapper.ToFile(orders"orders.xlsx");

一行代码,你的 List<Order> 就变成了一张带表头、带边框、自动列宽的 Excel 表格。表和表之间还自动展开展开嵌套对象:


嵌套对象 → 父子合并表头

publicclassOrder{publicintId { getset; }publicDemoCustomerCustomerInfo { getset; }  // 嵌套对象}publicclassDemoCustomer{publicstringName { getset; }publicstringCity { getset; }publicstringPhone { getset; }}

导出后表头自动变成:

          CustomerInfo  ├──────┼──────┼──────   Name  │ City │ Phone

不需要手写合并逻辑,全部自动推断。


数组/List → 独立 Sheet

当一个属性是 List<T> 类型时,直接写入一个单元格会超出 Excel 32767 字符限制。本库的做法:

子 Sheet 第一列自动关联回父行(默认取父类的 Id 属性),保证数据可追溯。


[ExcelDisplay] 自定义中文标题

不需要手写 PropertyInfo、字符串字典,直接在属性上标注:

publicclassOrder{    [ExcelDisplay("订单编号")]publiclongId { getset; }    [ExcelDisplay("客户名称")]publicstringCustomer { getset; }    [ExcelDisplay("订单金额")]publicdecimalAmount { getset; }    [ExcelDisplay("订单明细")]publicList<OrderItem>Items { getset; }}

效果:

  • 主表表头:订单编号 | 客户名称 | 订单金额 | 订单明细

  • 子 Sheet 名:Orders.订单明细(而非英文 Items)

  • 子 Sheet 关联列标题:订单编号(而非 Id)

这是独立的 ExcelDisplayAttribute,不会和 [Description][Config] 等其他特性冲突。


关联列智能匹配

子 Sheet 的第一列用来标识"这条明细属于哪个父行"。默认逻辑:

  1. 如果ChildSheetParentProperty 指定了属性名 → 用该属性

  2. 否则 自动扫描以 Id 结尾的属性(忽略大小写):Id / Uid / Vid / GUID …

  3. 列标题自动取该属性的 [ExcelDisplay],无则用属性名

// 自定义关联属性varstyle=newExcelStyle { ChildSheetParentProperty="Customer" };ExcelMapper.Build(orders).WithStyle(style).ToFile("output.xlsx");// 子 Sheet 第一列显示客户名,列标题自动取 [ExcelDisplay("客户名称")]

自定义样式

varstyle=newExcelStyle{HeaderBold=true,                               // 表头加粗HeaderBackgroundColor="#2E75B6",               // 表头背景(蓝色)HeaderFontColor="#FFFFFF",                     // 表头字体(白色)AutoFitColumns=true,                           // 自动列宽ShowBorder=true,                               // 显示边框FreezeTopRow=true,                             // 冻结首行NumberFormat="N2",                             // 金额格式DateTimeFormat="yyyy-MM-dd HH:mm:ss"// 日期格式};ExcelMapper.Build(orders).WithStyle(style).ToFile("styled.xlsx");

Dictionary 导出

配置类不一定是实体类,可能是 Dictionary<string, object>

vardict=newDictionary<stringobject>{    ["Server"="192.168.1.1",    ["Port"=8080,    ["Database"="MyDB",};// 键少时(≤10)→ 宽表(键为列头,一行数据)ExcelMapper.DictionaryToFile(dict"config.xlsx");// 键多时 → 竖表(Key/Value 两列)varlarge=newDictionary<stringstring>();for (inti=1i<=20i++)large[$"Key_{i:D2}"=$"Value_{i:D2}";ExcelMapper.DictionaryToFile(large"large_config.xlsx");

自动判断宽表/竖表,没有冗余配置。


Fluent Builder API

链式调用,语义清晰:

ExcelMapper.Build(orders)    .WithSheetName("订单汇总")    .WithStyle(ExcelStyle.Default)    .ToFile("output.xlsx");

也可以直接输出到流:

usingvarstream=newMemoryStream();ExcelMapper.ToStream(ordersstream"Orders");

完整属性一览

属性默认值说明
HeaderBoldtrue表头加粗
HeaderBackgroundColor"#4472C4"表头背景色
HeaderFontColor"#FFFFFF"表头字体颜色
AutoFitColumnstrue自动调整列宽
ShowBordertrue显示边框
FreezeTopRowtrue冻结表头行
FreezeFirstColumnfalse冻结首列
NumberFormatnull数字格式(如 "N2")
DateTimeFormatnull日期格式
ChildSheetParentPropertynull子 Sheet 关联列的属性名

适用场景

  • 财务系统:导出账单、报表、对账数据

  • ERP/CRM:导出客户列表、订单明细、库存清单

  • 数据分析:将查询结果快速导出给业务人员

  • 日志导出:运维数据导出为 Excel 方便排查


不依赖 Excel、不写模板、不需要 COM,你的 C# 对象就是最好的数据定义。

        除了这些工具,还有一些其他工具类,我已经同步上传到了NuGet里。并将各个工具单独分开。如果只需要单独的某个工具的话可以单独引用所需要的包。

        拜托家人们点点赞,或者移步GitHub点个星,球球了。

        另外,QQ群我也已经建好了。有任何问题、建议或者想一起交流学习的话可以加群。群号公众号主页菜单就可以获取了


NuGet 包名:VassasCo.Utility.ExcelMapper

GitHub:https://github.com/vassasco/VassasCo.Utility