一个 cs 文件,200KB,凭什么成为 .NET 数据库访问的隐形冠军?
先问一个问题:你写过的数据库访问代码里,有多少是真正有价值的业务逻辑,又有多少是重复无聊的样板代码?
using var connection = new SqlConnection(connectionString); connection.Open(); using var command = new SqlCommand("SELECT * FROM Users WHERE Id = @Id", connection); command.Parameters.AddWithValue("@Id", 1); using var reader = command.ExecuteReader(); var user = new User(); while (reader.Read()) { user.Id = reader.GetInt32(0); user.Name = reader.GetString(1); user.Email = reader.GetString(2); // 几十个字段,一个个手动映射…… }打开连接、创建命令、添加参数、读取器遍历、字段逐个映射。就为了查一条用户记录,写了二十多行代码,其中 90% 是纯粹的体力活。
更难受的是,项目里到处都是这种代码,看着烦,改着累,测都不想测。
有没有一种方式,保留写 SQL 的自由,但把这些样板代码全部干掉?
有,它就是Dapper。
什么是 Dapper?
Dapper是 Stack Overflow 团队开发并开源的微型 ORM。它的核心功能就一句话:把 SQL 查询结果自动映射成 C# 对象,除此之外什么都不干。
官网:https://github.com/DapperLib/Dappe
https://github.com/DapperLib/Dappe
安装:dotnet add package Dapper
这个库有多夸张?整个核心就一个 cs 文件,编译完不到 200KB。没有上下文、没有迁移、没有变更跟踪、没有延迟加载。它就是 ADO.NET 上面盖了一层极薄的毯子,让你写 SQL 时少写 80% 的样板代码。
NuGet 下载量早已破亿,Stack Overflow 自己每天几千万次请求都跑在它上面。性能接近手写 ADO.NET,是 .NET 生态里最快的数据库访问方式之一。
完全免费,Apache 2.0 协议,商用无忧。
同一件事,Dapper 怎么做?
刚才那二十多行代码,用 Dapper 写:
using var connection = new SqlConnection(connectionString); var user = await connection.QueryFirstOrDefaultAsync<User>( "SELECT * FROM Users WHERE Id = @Id", new { Id = 1 });没了。就两行。SQL 你写的,参数你传的,结果自动映射成 User 对象。没有黑魔法,只有清爽。
再看几个日常操作:
// 查列表 var users = await connection.QueryAsync<User>( "SELECT * FROM Users WHERE Age > @Age", new { Age = 18 }); // 查单个值 var count = await connection.ExecuteScalarAsync<int>( "SELECT COUNT(*) FROM Users"); // 增删改 await connection.ExecuteAsync( "INSERT INTO Users (Name, Email) VALUES (@Name, @Email)", new { Name = "张三", Email = "zhangsan@qq.com" }); // IN 查询,参数自动展开 var users = await connection.QueryAsync<User>( "SELECT * FROM Users WHERE Id IN @Ids", new { Ids = new[] { 1, 2, 3, 4, 5 } });每一项操作都是你亲手写的 SQL,Dapper 只帮你做了两件事:参数化防注入、结果自动映射。不多不少,刚刚好。
Dapper 真正适合什么样的人?
Dapper 不是万金油,它有自己最对味的那群人:
愿意写 SQL 的人。你清楚每一条 SQL 在干什么,你写的查询你心里有数,不想被 ORM 自动生成的那坨“黑盒 SQL”坑一把。
追求极致性能的人。Dapper 的速度接近裸写 ADO.NET,是 .NET 里最快的数据库访问方式之一。查询性能敏感的接口,用它就对了。
做小项目、小工具、控制台脚本的人。不想引一堆依赖,一个 200KB 的包搞定所有数据库访问,爽快。
接手老系统的人。几十张表结构奇葩、存储过程成堆、数据库设计一言难尽。那种“代码先行、自动建表”的思路根本玩不转,Dapper 却能无缝贴上去。
需要精细控制事务和查询的人。复杂事务逻辑、存储过程调用、一次请求返回多个结果集,Dapper 全部透明可控,每一步都清楚明白。
反过来,如果你希望框架帮你建表、自动写 SQL、完全用对象思维操作数据库,那 Dapper 不适合你。每个工具都有它的主场,Dapper 的主场就是三个字:轻、快、透。
一些好用的进阶功能
多结果集,一次请求拿回多个表的数据:
using var multi = await connection.QueryMultipleAsync(@" SELECT * FROM Users WHERE Id = @Id; SELECT * FROM Orders WHERE UserId = @Id; ", new { Id = 1 }); var user = await multi.ReadFirstAsync<User>(); var orders = await multi.ReadAsync<Order>();事务控制,一切透明:
using var transaction = connection.BeginTransaction(); try { await connection.ExecuteAsync("UPDATE Users SET Status = @Status", new { Status = "Active" }, transaction); await connection.ExecuteAsync("UPDATE Orders SET Processed = 1", null, transaction); transaction.Commit(); } catch { transaction.Rollback(); throw; }存储过程也是主战场:
var result = await connection.QueryAsync<User>( "GetUsersByAge", new { MinAge = 18 }, commandType: CommandType.StoredProcedure);博主实战心得
用好 Dapper,这几条经验能让你少踩坑:
不要到处 new SqlConnection。封装一个简单的连接工厂或扩展方法,统一管理连接字符串,代码整洁很多。
QueryMultiple 能省网络开销。查主表带子表,别分两次请求,一个 QueryMultiple 一趟搞定。
善用 Dapper 的参数展开。
IN查询直接传数组,不用自己拼占位符,安全又省心。和存储过程是天生一对。老系统的业务逻辑都在存储过程里,Dapper 对存储过程支持一等一的好。
总结
Dapper 就像工具箱里那把最顺手的美工刀——小巧、锋利、没有任何多余功能。它不替你做任何决定,不给你任何限制,只负责把你的 SQL 结果干净利落地变成对象,剩下的全交给你。
如果你就是那个“我就想安安静静写点 SQL”的人,Dapper 会让你找回数据库操作最原始的快感:一切尽在掌控。