导出 Excel 是每个业务系统的刚需。COM/VSTO 太重,EPPlus 要商业授权,手写 OpenXML 太繁琐……这个轻量级 Excel 映射器,不需要任何模板文件,你的 C# 对象就是模板。
常见方案对比
| 方案 | 依赖 | 复杂度 | 授权 |
|---|---|---|---|
| COM Interop | Excel 安装 | 极高 | 需 Office |
| EPPlus 5+ | .NET | 中 | 商业授权 |
| OpenXML 裸写 | .NET | 极高 | MIT |
| NPOI | .NET | 中 | Apache |
| ExcelMapper | ClosedXML | 极低 | MIT |
基于 ClosedXML 封装,开源免费,API 简洁到极致。
30 秒上手
dotnet add package VassasCo.Utility.ExcelMapperpublicclassOrder{publicintId { get; set; }publicstringCustomer { get; set; }publicdecimalAmount { get; set; }publicDateTimeOrderDate { get; set; }}varorders=GetOrders();ExcelMapper.ToFile(orders, "orders.xlsx");
一行代码,你的 List<Order> 就变成了一张带表头、带边框、自动列宽的 Excel 表格。表和表之间还自动展开展开嵌套对象:
嵌套对象 → 父子合并表头
publicclassOrder{publicintId { get; set; }publicDemoCustomerCustomerInfo { get; set; } // 嵌套对象}publicclassDemoCustomer{publicstringName { get; set; }publicstringCity { get; set; }publicstringPhone { get; set; }}
导出后表头自动变成:
CustomerInfo ├──────┼──────┼────── Name │ City │ Phone
不需要手写合并逻辑,全部自动推断。
数组/List → 独立 Sheet
当一个属性是 List<T> 类型时,直接写入一个单元格会超出 Excel 32767 字符限制。本库的做法:


子 Sheet 第一列自动关联回父行(默认取父类的 Id 属性),保证数据可追溯。
[ExcelDisplay] 自定义中文标题
不需要手写 PropertyInfo、字符串字典,直接在属性上标注:
publicclassOrder{ [ExcelDisplay("订单编号")]publiclongId { get; set; } [ExcelDisplay("客户名称")]publicstringCustomer { get; set; } [ExcelDisplay("订单金额")]publicdecimalAmount { get; set; } [ExcelDisplay("订单明细")]publicList<OrderItem>Items { get; set; }}
效果:
主表表头:订单编号 | 客户名称 | 订单金额 | 订单明细
子 Sheet 名:Orders.订单明细(而非英文 Items)
子 Sheet 关联列标题:订单编号(而非 Id)
这是独立的
ExcelDisplayAttribute,不会和[Description]、[Config]等其他特性冲突。
关联列智能匹配
子 Sheet 的第一列用来标识"这条明细属于哪个父行"。默认逻辑:
如果
ChildSheetParentProperty指定了属性名 → 用该属性否则 自动扫描以
Id结尾的属性(忽略大小写):Id/Uid/Vid/GUID…列标题自动取该属性的
[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<string, object>{ ["Server"] ="192.168.1.1", ["Port"] =8080, ["Database"] ="MyDB",};// 键少时(≤10)→ 宽表(键为列头,一行数据)ExcelMapper.DictionaryToFile(dict, "config.xlsx");// 键多时 → 竖表(Key/Value 两列)varlarge=newDictionary<string, string>();for (inti=1; i<=20; i++)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(orders, stream, "Orders");
完整属性一览
| 属性 | 默认值 | 说明 |
|---|---|---|
HeaderBold | true | 表头加粗 |
HeaderBackgroundColor | "#4472C4" | 表头背景色 |
HeaderFontColor | "#FFFFFF" | 表头字体颜色 |
AutoFitColumns | true | 自动调整列宽 |
ShowBorder | true | 显示边框 |
FreezeTopRow | true | 冻结表头行 |
FreezeFirstColumn | false | 冻结首列 |
NumberFormat | null | 数字格式(如 "N2") |
DateTimeFormat | null | 日期格式 |
ChildSheetParentProperty | null | 子 Sheet 关联列的属性名 |
适用场景
财务系统:导出账单、报表、对账数据
ERP/CRM:导出客户列表、订单明细、库存清单
数据分析:将查询结果快速导出给业务人员
日志导出:运维数据导出为 Excel 方便排查
不依赖 Excel、不写模板、不需要 COM,你的 C# 对象就是最好的数据定义。
除了这些工具,还有一些其他工具类,我已经同步上传到了NuGet里。并将各个工具单独分开。如果只需要单独的某个工具的话可以单独引用所需要的包。
拜托家人们点点赞,或者移步GitHub点个星,球球了。
另外,QQ群我也已经建好了。有任何问题、建议或者想一起交流学习的话可以加群。群号公众号主页菜单就可以获取了
NuGet 包名:
VassasCo.Utility.ExcelMapperGitHub:https://github.com/vassasco/VassasCo.Utility
夜雨聆风