第七章:C# 技能详解
在 opengis-skills 仓库中,C# / .NET 技能占据了重要地位。这一分类共收录 8 个技能,覆盖了从 Web 后端框架、后台管理系统、ORM 数据访问层,到 Office 文档处理、电子表格控件和代码保护的完整 .NET 开发生态链。无论你是需要快速开发一个 Web API、搭建企业级后台管理系统、高效操作数据库、读写 Excel 文件,还是保护分发出去的 DLL 不被反编译,C# 技能分类都有对应的解决方案。
本章将从技能全景出发,逐技能深入解析每个工具的核心理念、关键 API、典型用法和适用场景,最后通过决策树帮助你快速定位所需技能。
7.1 C# 技能全景
7.1.1 技能总览表
C# 分类下共有 8 个技能,按功能类型可以划分为五大方向:Web 框架、后台管理系统、ORM 框架、Excel/文档处理和代码保护。每个技能都在对应目录下包含完整的章节化教程。
| 技能 | 目录 | 章节数 | 类型 | 说明 |
|---|---|---|---|---|
| furion | csharp/Furion/ |
20 章 | Web 框架 | .NET 极简 Web 应用框架,动态 API、规范化响应、零配置启动 |
| admin-net-backend | csharp/Admin.NET/ |
10 章 | 后台管理 | 基于 Furion + SqlSugar + Redis + SignalR 的 RBAC 后台 |
| admin-net-frontend | csharp/Admin.NET/ |
— | 前端 | Vue 3 + Vite + Element Plus + Pinia + TypeScript 前端 |
| sqlsugar | csharp/SqlSugar/ |
15 章 | ORM | 国产多数据库 ORM,支持 10+ 数据库,链式查询,读写分离 |
| sod | csharp/sod/ |
10 章 | ORM | PDF.NET SOD,ORM + SQL-MAP + OQL 三合一框架 |
| npoi | csharp/npoi/ |
18 章 | 文件操作 | Apache POI 的 .NET 移植,Excel/Word/PPT 读写 |
| reogrid | csharp/ReoGrid/ |
20 章 | 控件 | .NET 电子表格控件(WinForms/WPF),内置公式引擎 |
| dotnet-reactor | csharp/dotnet-reactor/ |
15 章 | 安全工具 | .NET 代码保护,混淆、加壳、反编译、许可证管理 |
7.1.2 五大方向与推荐学习路线
推荐学习路线一:.NET 企业开发入栈
furion(Web 框架)
└→ admin-net-backend(后台管理实战)
└→ sqlsugar(数据访问层)
└→ sod(深入 ORM 设计思想)
这条路线从 Web 框架开始,到完整后台系统实战,再到 ORM 的深入掌握,覆盖了 .NET 后端开发的核心技能。
推荐学习路线二:Office 文档处理
npoi(Excel/Word 读写)
└→ reogrid(交互式电子表格)
这条路线适合企业内部报表系统、数据导入导出系统、财务分析工具等场景。
推荐学习路线三:代码安全加固
dotnet-reactor(代码保护与混淆)
这条路线适合商业软件开发、ISV 授权管理、Unity 游戏保护等场景。
7.1.3 技能之间的引用关系
C# 技能之间存在紧密的依赖和引用关系,形成了一套完整的知识网络:
- admin-net-backend 构建在 furion + sqlsugar 之上,是这两个技能的实战级整合
- admin-net-frontend 是 admin-net-backend 的前端项目,提供 Vue 3 全家桶的完整实现
- sod 和 sqlsugar 是两种不同设计理念的 ORM,可以对比学习以理解 ORM 的本质
- reogrid 和 npoi 分别解决”动态编辑电子表格”和”静态读写 Office 文件”两个正交需求
- dotnet-reactor 可保护由上述任何框架生成的可执行文件
7.2 Web 框架:Furion
7.2.1 Furion 是什么
Furion 是由百小僧(MonkSoul)创建并开源的 .NET 应用开发框架,其核心理念是 “让 .NET 开发更简单,更通用,更流行”。它不是一个替代 ASP.NET Core 的重型框架,而是一组围绕 ASP.NET Core 生态的增强工具集,通过极简的 API 设计和零入侵的集成方式,帮助开发者快速构建高质量的 Web 应用和服务。
Furion 采用 MIT/Apache-2.0 双重开源许可证,截止 2026 年 6 月的最新稳定版为 4.9.9.2,全面支持 .NET 10,同时 5.0.0 预览版也在演进中。从 2020 年首次发布至今,Furion 累计在 GitHub 上收获了超过两万 Star,成为国内 .NET 社区最具影响力的框架之一。
7.2.2 核心概念
Furion 的功能体系围绕几个关键概念展开:
动态 API(Dynamic API Controller)
这是 Furion 最标志性的功能。在传统的 ASP.NET Core 开发中,你需要手动创建 Controller、标记路由特性、注入服务——这些步骤繁琐且重复。Furion 的动态 API 彻底颠覆了这个工作流:你只需要写一个普通的 Service 类,让它继承 IDynamicApiController 接口,Furion 就会在运行时自动扫描你的 Service,将其中的 public 方法暴露为 RESTful API 端点。
// 只需要三行:定义接口、实现方法、继承 IDynamicApiController
public class UserService : IDynamicApiController
{
private readonly SqlSugarClient _db;
public UserService(SqlSugarClient db)
{
_db = db;
}
// Furion 自动生成 GET /api/user
public List<User> GetList()
{
return _db.Queryable<User>().ToList();
}
// Furion 自动生成 POST /api/user
public User Add(UserDto input)
{
var user = input.Adapt<User>();
_db.Insertable(user).ExecuteReturnEntity();
return user;
}
// Furion 自动生成 DELETE /api/user/{id}
public void Delete(long id)
{
_db.Deleteable<User>().Where(u => u.Id == id).ExecuteCommand();
}
}
不需要写 Controller 文件,不需要写 [Route] 特性,不需要写 [HttpPost] 标记——写完 Service,API 就已经就绪。这相当于 Furion 帮你完成了从 Service 到 Controller 的自动映射层,将开发者的精力集中在业务逻辑上。
规范化响应(UnifyResult)
前后端分离架构中,API 的响应格式一致性是一个看似简单、实则容易失控的问题。张三写的接口返回 { data: ... },李四写的接口返回 { result: ..., code: 200 },前端需要针对不同格式写不同的处理逻辑。
Furion 的 UnifyResult 解决了这个问题:它自动将所有 API 的返回值包装为统一的 JSON 格式:
{
"status": 200,
"success": true,
"data": { "id": 1, "name": "张三" },
"message": "操作成功",
"timestamp": 1722441600000
}
无论你的 Service 方法返回什么类型(实体、列表、分页对象,甚至 void),Furion 都会自动套上这层规范化外壳。你还可以通过 [UnifyResult] 特性或配置文件自定义包装格式。这层自动化确保了所有 API 的一致性,也为全局异常处理和日志记录提供了单一入口。
零配置启动
传统的 ASP.NET Core 项目的 Program.cs 通常包含几十行样板代码:注册 Swagger、注册 JWT、注册 CORS、注册日志、注册控制器……每次新项目都要复制粘贴一遍。
Furion 的 Serve.Run() 一行代码替代了这些:
using Furion;
Serve.Run(
defaultBuilder: builder =>
{
// 仅需补充项目特有的配置
builder.Services.AddSqlSugar(/* ... */);
builder.Services.AddSingleton<JwtHandler>();
}
);
Furion 内置了所有常见的注册逻辑,通过约定优于配置的原则,将启动文件压缩到最小。
7.2.3 关键功能模块
Furion 的 20 章教程覆盖了以下关键模块,每个模块都是实际项目中的刚需:
依赖注入([FromServices])
Furion 在 ASP.NET Core 内置 DI 容器的基础上提供了增强:支持 [FromServices] 特性注入、属性注入、命名服务注册与解析。
JWT 认证
集成了完整的 JWT 认证流水线:Token 签发、刷新、过期处理、权限验证,通过 JwtHandler 抽象类和 [Security] 特性实现声明式授权。
定时任务
通过 [Period] 特性定义定时任务,支持 Cron 表达式,无需引入额外的调度库:
[Period("0/5 * * * *")] // 每 5 分钟执行一次
public async Task SyncUserData()
{
// 定时执行的业务逻辑
}
事件总线(EventBus)
提供进程内事件发布/订阅模型,解耦业务模块间的通信。支持同步和异步执行,适合日志记录、邮件发送、数据同步等场景。
远程请求(HttpRequest)
封装了 HttpClient 的创建和管理,提供流畅的 API 调用链:
var result = await "https://api.example.com/data"
.SetHeaders(new { Authorization = "Bearer xxx" })
.GetAsAsync<ApiResult>();
自动处理连接池复用、超时重试、异常降级。
EF Core 集成
Furion 在 EF Core 的基础上提供了 Code First 自动迁移、数据库上下文池化、动态过滤(如软删除和多租户过滤)等增强。
日志与审计
集成 Serilog 等日志框架,提供结构化的日志记录和审计追踪能力。
缓存管理
提供统一的缓存抽象,支持内存缓存和 Redis 分布式缓存,通过 [Cache] 特性实现声明式缓存。
多租户
支持数据库分离、Schema 分离、字段隔离三种多租户方案,通过 [MultiTenant] 特性自动处理租户上下文。
中间件与过滤器
提供了全局异常处理中间件、请求日志中间件、跨域处理等常用中间件的开箱即用配置。
7.2.4 适用场景与局限性
适合:
- 快速开发 Web API 后端服务
- 微服务架构中的单个服务
- 中小团队希望降低项目配置成本
- 需要前后端分离的标准化接口规范
不适合:
- 对框架”黑盒”行为零容忍的团队(Furion 做了大量自动约定)
- 需要完全手工控制 ASP.NET Core 底层细节的场景
- 已经深度定制了 Startup/Program.cs 的老项目(迁移成本高)
7.3 后台管理系统:Admin.NET
7.3.1 Admin.NET 是什么
Admin.NET 是 opengis-skills 中整合度最高的技能——它不是单个工具,而是一套完整的、前后端分离的通用权限开发框架。它的后端构建在 Furion + SqlSugar + Redis + SignalR 的技术栈之上,前端采用 Vue 3 + Vite + Element Plus + Pinia + TypeScript 全家桶。
Admin.NET 的核心理念是 “站在巨人肩膀上”——它不重复造轮子,而是将业界最优秀的开源组件整合为开箱即用的后台管理系统。无论是搭建企业内部管理系统、开发 SaaS 平台,还是作为新项目的脚手架,Admin.NET 都提供了 20+ 个内置功能模块和灵活的扩展机制。
7.3.2 后端架构(admin-net-backend)
后端基于 Furion 框架构建,继承了 Furion 的动态 API、规范化响应、JWT 认证等所有能力,并在此基础上叠加了企业级后台特有的功能模块。
核心功能模块矩阵
| 模块 | 功能描述 | 涉及技能 |
|---|---|---|
| 用户管理 | 企业用户和系统管理员的增删改查,支持职务、机构、角色、数据权限绑定 | Furion、SqlSugar |
| 角色管理 | 基于 RBAC 的权限模型,角色绑定菜单权限和数据授权范围 | SqlSugar |
| 菜单管理 | 目录、菜单、按钮三级权限树,支持动态路由生成 | Furion |
| 机构管理 | 公司组织架构的多层级树形维护,支持部门、子公司、分公司等 | SqlSugar |
| 职位管理 | 用户职务标签维护,作为组织管理的辅助维度 | SqlSugar |
| 字典管理 | 系统枚举值、固定数据字典的统一维护 | SqlSugar |
| 日志系统 | 访问日志(登录/退出)和操作日志(业务操作/异常),支持 ES 存储 | Furion、Elasticsearch |
| 定时任务 | 分布式作业调度,支持 Cron 表达式和集群部署 | Furion、Quartz |
| 文件管理 | 文件上传/下载/预览,支持本地存储、阿里云 OSS、MinIO 等多种后端 | Furion |
| 代码生成器 | 根据数据库表结构一键生成前后端全套代码(实体、Service、Controller、Vue 页面) | SqlSugar |
| 消息推送 | WebSocket 实时消息(基于 SignalR),邮件和短信发送 | SignalR、Furion |
| 系统配置 | 系统运行参数的动态维护,无需修改配置文件即可调整行为 | SqlSugar |
| 在线用户 | 查看当前在线用户列表,支持强制踢出 | SignalR、Redis |
| 服务监控 | 服务器 CPU、内存、磁盘运行状态实时监控 | System.Diagnostics |
| 通知公告 | 系统级通知和公告的发布与管理 | SqlSugar |
| 限流控制 | 基于令牌桶的接口访问频率限制 | Furion |
| 开放授权 | OAuth 2.0 标准协议,支持微信、Gitee 等第三方登录 | Furion |
多租户支持
Admin.NET 支持三种多租户模式:
- 独立数据库:每个租户拥有独立的数据库实例,物理隔离最高
- 共享数据库、独立 Schema:同一数据库下按 Schema 隔离
- 共享数据库、共享 Schema:通过租户 ID 字段行级隔离
租户识别通过请求头或子域名完成,数据权限自动过滤,开发者无需在业务代码中手写租户过滤逻辑。
数据权限控制
除了功能权限(RBAC),Admin.NET 还实现了数据权限——同一角色下的不同用户可能看到不同范围的数据。数据权限可配置到组织级别、用户级别或自定义 SQL 条件。
Redis 缓存
高频查询数据(字典、配置、用户权限)会缓存到 Redis,减少数据库压力。通过 SignalR 推送缓存变更通知,保证集群环境下缓存一致性。
WebSocket 实时消息
基于 SignalR 实现服务端推送:在线用户变更通知、系统消息推送、缓存同步信号都走 WebSocket 通道,前端无需轮询。
7.3.3 前端架构(admin-net-frontend)
前端项目的核心技术栈:
| 技术 | 版本 | 用途 |
|---|---|---|
| Vue | 3.x | 渐进式 JavaScript 框架 |
| Vite | 5.x | 下一代前端构建工具 |
| Element Plus | 2.x | 面向 Vue 3 的 UI 组件库 |
| TypeScript | 5.x | 类型安全的 JavaScript 超集 |
| Pinia | 2.x | Vue 3 官方状态管理库 |
| Vue Router | 4.x | Vue 3 官方路由库 |
响应式布局
基于 Element Plus 的布局组件实现四种导航模式:侧边栏、顶部菜单、混合布局和经典模式。支持移动端适配和拖拽调整侧边栏宽度。
动态路由(权限驱动)
菜单不是写死在路由配置里的。用户登录后,前端向后端请求当前用户有权限的菜单列表,然后在 vue-router 中动态注册路由。这意味着:
- 菜单即权限——后端配置什么菜单,用户就看到什么页面
- 按钮级权限——菜单下还可以定义按钮权限,控制页面内的操作按钮显隐
- 路由守卫——未登录或权限不足时自动重定向
Pinia 状态管理
全局状态(用户信息、权限列表、系统配置、主题设置)统一由 Pinia 管理。按功能模块拆分为多个 Store(useUserStore、usePermissionStore、useAppStore),通过 Composition API 风格的组织方式保持代码清晰。
国际化
内置中英文双语支持,通过 vue-i18n 实现。所有页面文案按模块拆分到独立的语言文件中,添加新语言只需新增一个 JSON 文件。
多主题切换
支持亮色/暗色主题切换,通过 CSS 变量和 Element Plus 的主题定制系统实现。主题配置持久化到 localStorage。
API 接口封装
前端通过 Axios 封装了统一的请求/响应拦截器:自动附加 Token、自动处理 Token 过期刷新、自动解析规范化响应体。所有后端 API 按模块组织为独立的 Service 文件。
7.3.4 Admin.NET 的定位
Admin.NET 不是一个”玩具项目”,而是经历过多个商业项目验证的生产级框架。它的价值在于:
- 节省 80% 的重复开发工作:用户/角色/权限/菜单/日志这些每个项目都要写的功能,Admin.NET 已经写好并优化过
- 标准化团队开发:统一的代码风格、统一的项目结构、统一的 API 规范
- 二次开发友好:代码生成器可以快速生成 CRUD 页面,然后手工调整复杂逻辑
7.3.5 适用场景
- 企业级后台管理系统(MIS、ERP、OA、CRM 等)
- SaaS 平台的后端管理端
- 需要快速交付的权限管理系统
- 作为新项目的脚手架空项目
7.4 ORM 框架
7.4.1 SqlSugar
7.4.1.1 SqlSugar 是什么
SqlSugar 是国产 .NET ORM 框架中的标杆产品,由中国开发者创建并维护。它的核心理念是 “让数据访问更简单、更高效、更优雅”,通过丰富的 Lambda 表达式 API 和链式查询语法,让 .NET 开发者可以完全不写 SQL(或者只在复杂场景下写 SQL)就能完成所有数据库操作。
SqlSugar 采用 Apache-2.0 开源许可,GitHub Star 数超 15000,NuGet 下载量数百万。截止 2026 年 6 月的最新稳定版为 5.1.4.215,支持从 .NET Core 3.1 到 .NET 10 的所有版本。
7.4.1.2 多数据库支持
SqlSugar 支持超过 10 种数据库,切换数据库只需修改一行连接字符串,不需要改动任何业务代码:
| 数据库 | 支持程度 | 典型场景 |
|---|---|---|
| SQL Server | 完整支持 | 传统企业 Windows 服务器环境 |
| MySQL / MariaDB | 完整支持 | 互联网主流场景 |
| PostgreSQL | 完整支持 | GIS 数据库、开源优先项目 |
| Oracle | 完整支持 | 大型金融、政府项目 |
| SQLite | 完整支持 | 桌面应用、移动端、测试环境 |
| DM(达梦) | 完整支持 | 国产化替代(政府、军工) |
| KDBND(人大金仓) | 完整支持 | 国产化替代 |
| Oscar(神通) | 完整支持 | 国产化替代 |
| OpenGauss | 完整支持 | 开源国产数据库 |
| GBase / TDengine 等 | 扩展支持 | 物联网、时序数据场景 |
这种多数据库兼容能力让 SqlSugar 成为国产化替代项目中的首选 ORM——你可以先在 SQL Server 上开发,部署时切换到达梦或人大金仓,一行业务代码都不用改。
7.4.1.3 核心功能
Code First 模式
先定义好 C# 实体类,SqlSugar 自动生成对应的数据库表结构。支持自动迁移——当你修改实体类(新增字段、修改类型)后,SqlSugar 可以对比现有表结构并自动执行差异 SQL:
// 定义实体
[SugarTable("sys_user")]
public class User
{
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public long Id { get; set; }
[SugarColumn(ColumnDataType = "nvarchar(50)")]
public string Name { get; set; }
public int Age { get; set; }
[SugarColumn(IsNullable = true)]
public string Email { get; set; }
public DateTime CreateTime { get; set; } = DateTime.Now;
}
// Code First 初始化
db.CodeFirst.InitTables(typeof(User));
链式查询
SqlSugar 的标志性语法是流畅的链式 API,让数据查询读起来像自然语言:
// 基础查询
var users = db.Queryable<User>()
.Where(u => u.Age > 18)
.OrderBy(u => u.Id, OrderByType.Desc)
.Select(u => new { u.Name, u.Age })
.ToList();
// 分页查询
var page = db.Queryable<User>()
.Where(u => u.Name.Contains("张"))
.ToPagedList(pageIndex: 1, pageSize: 20);
// 多表联查
var result = db.Queryable<User, Order, OrderDetail>((u, o, d) =>
new JoinQueryInfos(
JoinType.Left, u.Id == o.UserId,
JoinType.Left, o.Id == d.OrderId
))
.Where((u, o, d) => o.Amount > 100)
.Select((u, o, d) => new
{
UserName = u.Name,
OrderNo = o.OrderNo,
ProductName = d.ProductName,
d.Quantity
})
.ToList();
链式查询支持所有常见的 SQL 操作:WHERE、JOIN(内连接/左连接/右连接/全连接)、GROUP BY、HAVING、子查询、UNION、CTE(公用表表达式)。
读写分离
配置主库(写入)和从库(读取),SqlSugar 自动将查询操作路由到从库、写操作路由到主库:
// 配置连接
var config = new ConnectionConfig
{
ConnectionString = masterConnStr,
SlaveConnectionConfigs = new List<SlaveConnectionConfig>
{
new() { ConnectionString = slave1ConnStr, HitRate = 5 },
new() { ConnectionString = slave2ConnStr, HitRate = 3 }
}
};
支持多从库负载均衡(通过 HitRate 配置权重)和故障自动切换。
分表分库
大数据量场景下,SqlSugar 支持按时间(年月)、按租户 ID、按自定义规则进行分表。查询时通过 SplitTable 方法自动拼接多表查询:
// 按年月分表,格式为 Order_202401, Order_202402 ...
db.Insertable(order).SplitTable().ExecuteReturnEntity();
// 查询自动跨表合并
var list = db.Queryable<Order>()
.SplitTable(tabs => tabs.Where(y => y.Date.Year == 2024))
.Where(o => o.Status == 1)
.ToList();
导航属性
支持一对多、多对多的导航属性,通过 Include / ThenInclude 实现类似 EF Core 的饥饿加载:
var user = db.Queryable<User>()
.Includes(u => u.Orders) // 一对多
.Includes(u => u.Roles, u => u.Permissions) // 多对多
.Where(u => u.Id == 1)
.First();
AOP 拦截器
SqlSugar 内置 AOP 机制,可以在 SQL 执行前后插入自定义逻辑。常见用法包括:
- SQL 日志记录:拦截所有 SQL 执行,记录到日志文件或数据库
- 缓存拦截:查询前检查缓存,命中则返回缓存数据
- 数据加密:写入前加密敏感字段,读取后解密
- 性能监控:记录慢查询的执行时间
db.Aop.OnLogExecuting = (sql, pars) =>
{
Console.WriteLine($"SQL: {sql}");
Console.WriteLine($"Params: {string.Join(",", pars.Select(p => p.Value))}");
};
事务支持
支持声明式事务([UseTran] 特性)、编程式事务、异步事务和分布式事务(基于 CAP 理论)。
7.4.1.4 与其他 ORM 的对比
| 特性 | SqlSugar | EF Core | Dapper |
|---|---|---|---|
| 查询语法 | Lambda + 链式 API | LINQ | 原生 SQL |
| 多数据库切换 | 改连接串即用 | 改 Provider | 改连接串即用 |
| Code First | 支持 | 支持 | 不支持 |
| 分表分库 | 内置支持 | 需扩展 | 需手工实现 |
| 导航属性 | 内置支持 | 内置支持 | 需 MultiQuery |
| 性能 | 高(接近 Dapper) | 中等 | 最高 |
| 学习曲线 | 低 | 中 | 低 |
| 国产数据库 | 广泛支持 | 有限 | 依赖 ADO.NET |
7.4.2 SOD(PDF.NET SOD)
7.4.2.1 SOD 是什么
SOD(SQL-MAP、ORM、Data Controls)是一个拥有超过 15 年历史的国产企业级数据应用开发框架。它的名称源于三大核心模块的缩写:
- S — SQL-MAP:基于 XML 配置的 SQL 查询映射(类似 MyBatis)
- O — ORM:对象关系映射 + OQL(ORM Query Language)查询语言
- D — Data Controls:数据窗体控件,支持 WinForm / WebForm / WPF
SOD 的设计目标是 “对数据访问细节的全方位掌控”。不同于 SqlSugar 的”零 SQL”理念,SOD 走了一条兼容并包的路线:你既可以使用 ORM 自动生成 SQL,也可以使用 OQL 编写类型安全的查询,还可以使用 SQL-MAP 编写手写 SQL,甚至可以三种方式混合使用。
7.4.2.2 三种数据访问模式
模式一:ORM 模式
// 查询单个实体
User user = db.Query<User>(1); // 通过主键查询
// 插入
User newUser = new User { Name = "李四", Age = 25 };
db.Add(newUser);
// 更新
user.Name = "李四(修改)";
db.Update(user);
模式二:OQL 查询语言
OQL(ORM Query Language)是 SOD 独创的、类似 LINQ 但更接近 SQL 语义的查询语言:
// OQL 查询
User user = OQL.From<User>()
.Where(u => u.Age > 18 && u.Name.Contains("张"))
.OrderBy(u => u.CreateTime, "desc")
.Select()
.ToEntity();
// OQL 支持 JOIN
var result = OQL.From<User>()
.InnerJoin<Order>().On(u => u.Id == o.UserId)
.Where<Order>(o => o.Amount > 500)
.Select()
.ToList<dynamic>();
OQL 的特点是 Select() 后面的 .End 或 .ToEntity() 作为终止符,语法风格自成一派。
模式三:SQL-MAP 模式
这是 SOD 最独特的特性——像 Java MyBatis 一样,将 SQL 语句写在 XML 配置文件中:
<!-- UserSQLMap.xml -->
<SqlMap>
<Select CommandName="GetUsersByAge">
<CommandText>
SELECT Id, Name, Age, Email
FROM sys_user
WHERE Age > @MinAge
</CommandText>
<Parameters>
<Param Name="MinAge" Type="int" />
</Parameters>
</Select>
</SqlMap>
// C# 调用
var users = db.Query<User>("GetUsersByAge", new { MinAge = 18 });
SQL-MAP 模式的价值在于:
- SQL 与代码分离:DBA 可以直接优化 SQL 而无视 C# 代码
- 复杂 SQL 支持:存储过程调用、递归 CTE、复杂的多表联查都可以写进 XML
- 热更新:修改 XML 不需要重新编译项目
7.4.2.3 SOD 与 SqlSugar 的对比
| 维度 | SqlSugar | SOD |
|---|---|---|
| 设计哲学 | “零 SQL”,链式 API 至上 | “全掌控”,多模式混合 |
| 主要查询方式 | Lambda 链式 | ORM / OQL / SQL-MAP 三选一 |
| SQL 手写 | 不鼓励,但支持 | 鼓励通过 SQL-MAP 手写 |
| XML 映射 | 不支持 | 核心特性(类似 MyBatis) |
| 历史 | 2015 年至今 | 2006 年至今 |
| 社区活跃度 | 极高(15K+ Star) | 中等 |
| 学习曲线 | 低 | 中(三种模式需要理解) |
| 最适合 | 快速开发、多数据库切换 | 金融、复杂 SQL、遗留系统迁移 |
选择建议:
- 如果你是 .NET 新手、项目需要快速交付——选 SqlSugar
- 如果你的系统需要复杂的手写 SQL、DBA 需要直接修改查询逻辑——选 SOD
- 如果你来自 Java 背景、习惯 MyBatis 的开发模式——SOD 的 SQL-MAP 会让你倍感亲切
7.5 Excel / 文档处理
7.5.1 NPOI
7.5.1.1 NPOI 是什么
NPOI 是 Apache POI 项目的 .NET 移植版,由 Tony Qu(屈喆)创建,用于在 不安装 Microsoft Office 的情况下读写 Excel、Word、PowerPoint 文件。它完全开源免费(Apache 2.0 许可),跨平台支持 Windows、Linux、macOS,是 .NET 生态中最成熟的 Office 文件处理库。
NPOI 的名称含义:
- N — .NET 平台
- POI — 源自 Apache POI(Poor Obfuscation Implementation,对 Microsoft OLE2 格式的一种幽默称呼)
7.5.1.2 支持的文件格式
| 类型 | 格式 | 扩展名 | NPOI 核心类 | 说明 |
|---|---|---|---|---|
| Excel 97-2003 | BIFF8 二进制 | .xls |
HSSFWorkbook |
最大 65536 行,256 列 |
| Excel 2007+ | OpenXML | .xlsx |
XSSFWorkbook |
最大 1048576 行,16384 列 |
| Excel 流式写入 | OpenXML | .xlsx |
SXSSFWorkbook |
大数据量导出,内存友好 |
| Word 2007+ | OpenXML | .docx |
XWPFDocument |
段落、表格、图片 |
| Word 97-2003 | 二进制 | .doc |
HWPFDocument |
有限支持 |
| PPT 2007+ | OpenXML | .pptx |
XMLSlideShow |
幻灯片、形状、文本 |
| PPT 97-2003 | 二进制 | .ppt |
HSLFSlideShow |
有限支持 |
7.5.1.3 Excel 读写核心操作
创建工作簿与工作表
using NPOI.XSSF.UserModel;
// 创建 .xlsx 工作簿
var workbook = new XSSFWorkbook();
var sheet = workbook.CreateSheet("员工信息表");
// 创建表头行
var headerRow = sheet.CreateRow(0);
headerRow.CreateCell(0).SetCellValue("编号");
headerRow.CreateCell(1).SetCellValue("姓名");
headerRow.CreateCell(2).SetCellValue("年龄");
headerRow.CreateCell(3).SetCellValue("入职日期");
// 填充数据行
var dataRow = sheet.CreateRow(1);
dataRow.CreateCell(0).SetCellValue(1);
dataRow.CreateCell(1).SetCellValue("张三");
dataRow.CreateCell(2).SetCellValue(28);
// 日期需要特殊处理
var dateCell = dataRow.CreateCell(3);
dateCell.SetCellValue(DateTime.Now);
var dateStyle = workbook.CreateCellStyle();
dateStyle.DataFormat = workbook.CreateDataFormat().GetFormat("yyyy-MM-dd");
dateCell.CellStyle = dateStyle;
// 保存到文件
using var fs = File.Create("员工信息.xlsx");
workbook.Write(fs);
读取 Excel 文件
using var fs = File.OpenRead("员工信息.xlsx");
var workbook = new XSSFWorkbook(fs);
var sheet = workbook.GetSheetAt(0);
for (int row = 1; row <= sheet.LastRowNum; row++) // 跳过表头
{
var currentRow = sheet.GetRow(row);
if (currentRow == null) continue;
var id = (int)currentRow.GetCell(0).NumericCellValue;
var name = currentRow.GetCell(1).StringCellValue;
var age = (int)currentRow.GetCell(2).NumericCellValue;
// 处理数据...
}
单元格样式
NPOI 支持丰富的单元格样式控制:
var style = workbook.CreateCellStyle();
// 字体
var font = workbook.CreateFont();
font.FontName = "微软雅黑";
font.FontHeightInPoints = 12;
font.IsBold = true;
font.Color = IndexedColors.White.Index;
style.SetFont(font);
// 背景色
style.FillForegroundColor = IndexedColors.DarkBlue.Index;
style.FillPattern = FillPattern.SolidForeground;
// 边框
style.BorderTop = BorderStyle.Thin;
style.BorderBottom = BorderStyle.Thin;
style.BorderLeft = BorderStyle.Thin;
style.BorderRight = BorderStyle.Thin;
// 对齐
style.Alignment = HorizontalAlignment.Center;
style.VerticalAlignment = VerticalAlignment.Center;
cell.CellStyle = style;
合并单元格
// 合并 A1 到 D1(第 0 行,第 0 列到第 3 列)
sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, 3));
公式计算
NPOI 支持 Excel 公式的写入和求值:
cell.SetCellFormula("SUM(A2:A10)"); // 写入公式
// 求值公式
var evaluator = new XSSFFormulaEvaluator(workbook);
var cellValue = evaluator.Evaluate(cell);
double result = cellValue.NumberValue;
大数据量导出(SXSSFWorkbook)
当需要导出几十万行数据时,XSSFWorkbook 会把整个表格加载到内存中,导致 OOM。SXSSFWorkbook 通过流式(窗口化)写入解决了这个问题:
var workbook = new SXSSFWorkbook(100); // 内存中只保留 100 行
var sheet = workbook.CreateSheet();
for (int i = 0; i < 500000; i++)
{
var row = sheet.CreateRow(i);
row.CreateCell(0).SetCellValue(i);
// ...
}
// 写入文件
using var fs = File.Create("大数据.xlsx");
workbook.Write(fs);
workbook.Dispose(); // 清理临时文件
图表
NPOI 支持绘制折线图、柱状图、饼图等常见图表。通过 XSSFDrawing 在工作表上创建绘图区域,然后添加图表系列并绑定数据区域。
7.5.1.4 Word 操作
using NPOI.XWPF.UserModel;
var doc = new XWPFDocument();
var para = doc.CreateParagraph();
var run = para.CreateRun();
run.SetText("这是标题");
run.SetBold(true);
run.SetFontSize(18);
// 表格
var table = doc.CreateTable();
var row = table.GetRow(0);
row.GetCell(0).SetText("列1");
row.GetCell(1).SetText("列2");
using var fs = File.Create("文档.docx");
doc.Write(fs);
7.5.1.5 NPOI 的适用场景
- 后台系统的数据导出(用户列表、订单报表、财务报表)
- 批量数据导入(学生成绩、员工信息、产品目录)
- 自动化报表生成(日报、周报、月报的定时生成)
- Word 模板填充(合同、通知书、证书的批量生成)
- 不需要 Microsoft Office 的服务器端文档处理
7.5.2 ReoGrid
7.5.2.1 ReoGrid 是什么
ReoGrid 是一个开源的 .NET 电子表格控件,由 Unvell 团队开发维护。它解决了 NPOI 无法解决的问题——在应用程序内部提供类似于 Excel 的可视化编辑体验。
NPOI 做的是”静”——读写文件,生成一个 Excel 文件或者从一个 Excel 文件读取数据。ReoGrid 做的是”动”——在你的 WinForms 或 WPF 应用里嵌入一个完整的表格编辑器,用户可以像操作 Excel 一样编辑单元格、输入公式、设置格式、拖拽填充。
ReoGrid 的核心价值在于:
传统 Excel 方案 ReoGrid 方案
───────────────── ─────────────────
用户打开 Excel → 编辑 → 保存 应用内嵌表格控件 → 用户直接编辑
→ 上传回系统 → 数据直接在数据库中
依赖 Office 安装 无需安装 Office
无法控制编辑行为 可编程控制每个单元格的行为
7.5.2.2 核心特性
300+ 内置函数
ReoGrid 的公式引擎内置了超过 300 个函数,覆盖了常用 Excel 函数的 90% 以上:
| 函数类别 | 代表函数 |
|---|---|
| 数学 | SUM, AVERAGE, MAX, MIN, ROUND, ABS, MOD |
| 统计 | COUNT, COUNTA, COUNTIF, SUMIF, AVERAGEIF |
| 查找 | VLOOKUP, HLOOKUP, INDEX, MATCH, INDIRECT |
| 逻辑 | IF, AND, OR, NOT, IFERROR, IFNA |
| 文本 | LEFT, RIGHT, MID, CONCATENATE, TRIM, UPPER |
| 日期 | TODAY, NOW, YEAR, MONTH, DAY, DATEDIF |
| 财务 | PMT, FV, NPV, IRR, RATE |
公式引擎支持单元格引用(=A1+B2)、跨工作表引用(=Sheet2!A1)和区域引用(=SUM(A1:A10))。公式改变时自动触发重算。
自定义公式
你可以注册自己的公式函数,扩展 ReoGrid 的计算能力:
// 注册自定义函数:计算增值税
worksheet.RegisterFunction("VAT", (args) =>
{
var amount = (double)args[0];
var rate = args.Length > 1 ? (double)args[1] : 0.13;
return amount * rate;
});
// 现在可以在单元格中使用 =VAT(A1) 或 =VAT(A1, 0.09)
数据绑定
支持将 DataTable、List、Dictionary 等数据源直接绑定到表格区域:
var dataTable = GetUserListFromDatabase();
worksheet["A1"] = dataTable; // 自动填充表头和所有行
数据绑定支持双向同步——修改单元格后数据源自动更新。
条件格式
根据单元格的值动态应用样式,支持数据条、色阶和图标集:
// 销售额大于 10000 的标记为绿色
var rule = new ConditionRule
{
Condition = new GreaterThanCondition(10000),
Style = new WorksheetRangeStyle
{
Flag = PlainStyleFlag.BackColor,
BackColor = Color.LightGreen
}
};
worksheet.SetConditionalFormat("B2:B100", rule);
冻结窗格与视图控制
worksheet.FreezeToCell(1, 1); // 冻结首行和首列
图表与可视化
内置柱状图、折线图、饼图等基本图表类型,可直接嵌入表格中。
事件处理
worksheet.CellDataChanged += (sender, args) =>
{
var cell = args.Cell;
Console.WriteLine($"单元格 {cell.Address} 的值变更为: {cell.Data}");
// 可以在此触发数据校验或自动计算
};
Excel 文件导入导出
既可以通过 NPOI 等库实现导入导出,也可以使用 ReoGrid 自带的文件支持读写 .xlsx 文件(注意:是 ReoGrid 的 .rgf 格式和 .xlsx 格式)。
7.5.2.3 ReoGrid 与 NPOI 的对比
| 维度 | NPOI | ReoGrid |
|---|---|---|
| 核心功能 | 读写 Office 文件(静态) | 可视化电子表格控件(动态) |
| 使用方式 | NuGet 库,纯代码操作 | 拖拽控件到窗体 + 代码控制 |
| UI 能力 | 无 | 完整的 Excel 式界面 |
| 公式引擎 | 支持求值但不展示 | 内置功能完整的公式引擎 |
| 用户交互 | 不支持 | 用户可直接编辑单元格 |
| 文件格式 | .xls / .xlsx / .docx / .pptx | .xlsx 读写 |
| 平台 | 任意 .NET 平台 | WinForms + WPF |
| 适用场景 | 服务端生成/解析文件 | 桌面应用内嵌表格编辑器 |
选择建议:
- 如果你的需求是”生成一个 Excel 文件让用户下载”——选 NPOI
- 如果你的需求是”在应用里做一个能编辑的表格界面”——选 ReoGrid
- 如果两者都需要——NPOI 负责后台文件生成,ReoGrid 负责前台表格编辑
7.6 代码保护:.NET Reactor
7.6.1 .NET Reactor 是什么
.NET Reactor 是由 Eziriz 公司开发的专业 .NET 代码保护和软件许可工具。它的使命是保护 .NET 程序集(EXE 和 DLL)免受反编译、逆向工程和未授权使用的威胁。
要理解 .NET Reactor 的价值,首先要理解 .NET 程序的一个”先天缺陷”:.NET 编译后的产物不是原生机器码,而是 IL(Intermediate Language,中间语言)字节码。IL 代码保留了类名、方法名、参数名、局部变量名等丰富的元数据——足够让反编译工具(如 ILSpy、dnSpy、dotPeek)近乎完美地还原出可读的 C# 源代码。
这意味着你辛辛苦苦写的商业逻辑、核心算法,分发给客户后,任何一个稍微懂 .NET 的人都能拿到近乎原始代码。
.NET Reactor 解决的就是这个问题。
7.6.2 保护技术的层次体系
.NET Reactor 提供从浅到深的多层保护机制。合理的保护策略不是”无脑全开”(会影响性能),而是根据保护需求逐层选择:
第一层:混淆(Obfuscation)
混淆不改变代码逻辑,但让反编译后的代码变得难以阅读。
- 重命名混淆:将类名、方法名、变量名替换为无意义的短字符串。例如
UserManager.GetUserById()被重命名为a.b()。 - 控制流混淆:对方法的 IL 代码进行控制流变换(插入无用分支、打乱顺序),让反编译器的逻辑还原彻底失效。
- 字符串加密:代码中的字符串常量(数据库连接串、加密密钥、URL)在编译后被加密存储,运行时动态解密。
- 资源加密:嵌入的文本资源、配置文件同样被加密。
第二层:加密与加壳
- 程序集加密:将整个 DLL 加密,只有在运行时才解密加载到内存。
- 加壳(Packing):将多个依赖 DLL 合并为一个可执行文件,简化分发的同时增加分析难度。
- NecroBit 技术:将 IL 代码的关键部分转换为不可读的字节码,反编译器直接报错,而非显示混淆后的代码。这是 .NET Reactor 区别于普通混淆工具的杀手级特性。
第三层:本地代码编译(Native Compilation)
将 IL 方法转换为 x86/x64 原生机器码。原生代码的逆向难度远高于 IL 代码——IL 反编译工具对此完全失效,逆向工程师需要面对汇编级别的分析。
第四层:反调试与防篡改
- 反调试检测:运行时检测是否被调试器附加(如 Visual Studio、dnSpy),检测到则终止或执行反制逻辑。
- 防篡改签名:程序启动时检查自身文件哈希,如果被修改(比如注入破解代码),拒绝运行。
- 内存保护:防止运行时代码注入和内存 dump。
7.6.3 许可证管理系统
.NET Reactor 不只是代码保护工具,还内置了一套完整的 软件许可管理系统(Automation License System):
- 硬件锁定:通过 CPU ID、硬盘序列号、MAC 地址等绑定到特定机器
- 试用版管理:限制试用天数、使用次数、到期日期
- 功能授权:不同许可证级别解锁不同功能模块
- 订阅续费:定期验证许可证有效性
- 在线激活:通过 Web API 激活和反激活
- 离线激活:支持无网络环境的许可证文件激活
许可证验证逻辑被嵌入到保护后的程序集中,与代码保护协同工作——破解许可证首先需要攻破代码保护,形成双重防线。
7.6.4 命令行与 CI/CD 集成
.NET Reactor 提供完整的命令行接口,可以集成到 CI/CD 流水线中,实现每次构建自动保护:
# 命令行保护示例
dotnet_reactor.exe -project myapp.nrproj -build
.nrproj 是 .NET Reactor 的项目文件,保存了所有保护设置。团队可以在版本库中提交这个配置文件,确保所有开发者和 CI 环境使用一致的保护策略。
7.6.5 适用场景
- 商业 .NET 桌面软件(ERP、财务软件、工具软件)的保护
- Unity 游戏的反破解保护
- ISV(独立软件供应商)的许可证管理
- 企业内部工具的保护(防止员工逆向出敏感逻辑)
- 加密狗/硬件锁的软件配合保护
7.6.6 注意事项
- 性能折中:越强的保护(特别是 NecroBit 和本地编译),运行时性能开销越大。建议核心算法高强度保护,UI 层轻量保护
- 调试困难:保护后的程序几乎无法调试,保留一份未保护的版本用于测试和排错
- 杀毒软件误报:加壳后的程序可能被杀毒软件误判为恶意软件,需要提交白名单
- .NET 版本兼容:确认 .NET Reactor 版本支持你使用的 .NET 版本(.NET 5/6/7/8/9/10)
7.7 C# 技能决策树
当你在 .NET 开发中遇到需求时,可以按照以下决策树快速定位应该使用哪个技能:
需要 C# / .NET 技能?
│
├─ 快速开发 Web API 服务
│ └─ furion
│ 含:动态 API、JWT 认证、缓存、事件总线、定时任务
│
├─ 搭建后台管理系统
│ │
│ ├─ 后端(权限/用户/菜单/日志/代码生成)
│ │ └─ admin-net-backend
│ │ 基于 Furion + SqlSugar + Redis + SignalR
│ │
│ └─ 前端(Vue 3 管理界面)
│ └─ admin-net-frontend
│ 基于 Vue 3 + Vite + Element Plus + Pinia + TypeScript
│
├─ 数据库操作 / ORM
│ │
│ ├─ 快速开发,多数据库切换
│ │ └─ sqlsugar
│ │ 链式 API、Code First、分表分库、导航属性
│ │
│ └─ 复杂 SQL,需要 SQL 与代码分离
│ └─ sod
│ SQL-MAP(类似 MyBatis)+ OQL 查询语言
│
├─ Office 文档处理
│ │
│ ├─ 批量生成/解析 Excel、Word、PPT 文件
│ │ └─ npoi
│ │ 服务端读写,无需安装 Office
│ │
│ └─ 桌面应用内嵌可交互的电子表格
│ └─ reogrid
│ 可视化编辑、公式引擎、WinForms / WPF
│
└─ 保护 .NET 程序不被反编译
└─ dotnet-reactor
混淆、NecroBit、本地编译、许可证管理
常见组合方案:
| 业务场景 | 推荐技能组合 |
|---|---|
| 从零搭建企业后台 | furion → admin-net-backend + admin-net-frontend |
| 已有项目,需要 ORM | sqlsugar(轻量)或 sod(重 SQL) |
| 已有项目,需要数据导出 | npoi |
| 已有项目,需要表格编辑界面 | reogrid(WinForms/WPF) |
| 产品发布前 | dotnet-reactor(代码保护 + 许可证) |
| 全栈开发自学路径 | furion → sqlsugar → admin-net-backend → npoi → dotnet-reactor |
7.8 本章小结
C# 技能分类虽然只有 8 个技能,但它们覆盖了 .NET 生态中从开发框架到生产保护的全链路。这 8 个技能不是孤岛,而是一张知识网络:
- furion 是 WEB 开发的”地基”,提供了动态 API、依赖注入、认证等基础设施
- admin-net-backend 和 admin-net-frontend 是在地基上建好的”毛坯房”——开箱即用的后台管理系统
- sqlsugar 和 sod 解决了最核心的数据访问问题,提供了两种不同设计理念的 ORM 方案
- npoi 和 reogrid 分别处理 Office 文档的”静态读写”和”动态编辑”,覆盖了报表和电子表格两大类需求
- dotnet-reactor 是”封条”,在软件交付的最后一环保障代码安全
无论你处于 .NET 开发旅程的哪个阶段——初学者通过 furion 快速上手 Web API,中级开发者通过 admin-net-backend 学习完整项目架构,高级工程师通过 sqlsugar 和 sod 理解 ORM 设计思想,商业软件作者通过 dotnet-reactor 保护成果——C# 技能分类都能提供对应的知识支撑。
下一章将进入 GIS 技能详解,带你深入 GDAL、GeoServer、PostGIS 等地理信息系统的核心技能。