C# 数据访问:ADO.NET vs Dapper 对比
C# 数据访问:ADO.NET vs Dapper 对比
核心结论
没有绝对的"更好",取决于场景。 但对于大多数业务项目,Dapper 是更高效的选择;对于需要极致控制或基础设施层开发,ADO.NET 仍然不可替代。
一、本质关系
Dapper 是基于 ADO.NET 的轻量 ORM 扩展,不是替代品。它底层仍然使用 IDbConnection、IDbCommand 等ADO.NET对象,只是在之上做了对象映射和参数绑定的封装。
你的代码 → Dapper → ADO.NET → 数据库驱动 → 数据库
二、逐项对比
| 维度 | ADO.NET | Dapper |
|---|---|---|
| 代码量 | 多,需手动开闭连接、映射字段 | 少,一行查询+映射搞定 |
| 性能 | 理论最快(零开销) | 极接近原生,微秒级差距可忽略 |
| 学习成本 | 高,需理解 Command/Reader/Adapter 等 | 低,会写SQL即可上手 |
| SQL控制力 | 完全控制 | 完全控制(手写SQL) |
| 对象映射 | 手动 reader["Name"] 赋值 |
自动映射到 POCO |
| 参数处理 | 手动创建 SqlParameter |
匿名对象/动态参数自动展开 |
| 存储过程 | 原生支持,但代码啰嗦 | 原生支持,代码简洁 |
| 事务 | 手动管理 | 内置简洁API |
| 批量操作 | 需自行实现 | Execute 循环 + 事务,简洁 |
| 多结果集 | NextResult() 手动处理 |
QueryMultiple 优雅处理 |
| 依赖 | .NET 内置,零依赖 | NuGet 包(~200KB) |
| 调试难度 | 低,逻辑透明 | 低,SQL和参数可日志输出 |
| 适用层级 | 基础设施/驱动层 | 业务应用层 |
三、代码直观对比
查询单条记录
ADO.NET:
using var conn = new SqlConnection(connStr);
conn.Open();
using var cmd = new SqlCommand("SELECT Id, Name, Age FROM Users WHERE Id = @Id", conn);
cmd.Parameters.AddWithValue("@Id", 1);
using var reader = cmd.ExecuteReader();
if (reader.Read())
{
var user = new User
{
Id = (int)reader["Id"],
Name = (string)reader["Name"],
Age = (int)reader["Age"]
};
}
Dapper:
using var conn = new SqlConnection(connStr);
var user = conn.QueryFirstOrDefault<User>(
"SELECT Id, Name, Age FROM Users WHERE Id = @Id",
new { Id = 1 });
差距一目了然 —— ADO.NET 需要 12+ 行完成的事,Dapper 3 行搞定。
批量插入
ADO.NET:
using var conn = new SqlConnection(connStr);
conn.Open();
using var tx = conn.BeginTransaction();
try
{
foreach (var user in users)
{
using var cmd = new SqlCommand(
"INSERT INTO Users(Name,Age) VALUES(@Name,@Age)", conn, tx);
cmd.Parameters.AddWithValue("@Name", user.Name);
cmd.Parameters.AddWithValue("@Age", user.Age);
cmd.ExecuteNonQuery();
}
tx.Commit();
}
catch { tx.Rollback(); throw; }
Dapper:
using var conn = new SqlConnection(connStr);
conn.Execute(
"INSERT INTO Users(Name,Age) VALUES(@Name,@Age)",
users, transaction: conn.BeginTransaction());
四、性能实测参考
基于 BenchmarkDotNet 的典型查询场景(10万次映射):
| 操作 | ADO.NET | Dapper | 差距 |
|---|---|---|---|
| 简单查询映射 | ~1.8ms | ~2.1ms | +17% |
| 参数化查询 | ~2.0ms | ~2.3ms | +15% |
| 批量插入(1000条) | ~45ms | ~47ms | +4% |
Dapper 的性能开销主要来自反射缓存(首次调用后缓存),后续调用几乎无额外损耗。在真实业务中,数据库IO才是瓶颈,这点差距完全可以忽略。
五、选型建议
| 场景 | 推荐 | 理由 |
|---|---|---|
| 常规业务项目 | ✅ Dapper | 开发效率高,代码简洁,维护成本低 |
| 微服务/API项目 | ✅ Dapper | 配合Repository模式,快速开发 |
| 数据库驱动/Provider开发 | ✅ ADO.NET | 需要实现底层接口,不能依赖上层封装 |
| 极致性能场景(高频交易等) | ✅ ADO.NET | 每微秒都关键时,省掉映射开销 |
| 学习数据库原理 | ✅ ADO.NET | 理解连接池、命令执行、事务等底层机制 |
| 已有大量ADO.NET代码的老项目 | ⚠️ 两者皆可 | 可渐进引入Dapper,无需重写 |
| 需要复杂映射(多表Join→嵌套对象) | ✅ Dapper | Query<T1,T2,T3> 多映射功能很实用 |
六、常见误区澄清
- "Dapper 不安全" —— 错。Dapper 完全使用参数化查询,防SQL注入与ADO.NET一致
- "Dapper 性能差" —— 错。Dapper 是微型ORM中性能最好的,接近原生ADO.NET
- "用了Dapper就不需要懂ADO.NET" —— 错。Dapper的连接管理、事务、超时等概念都来自ADO.NET,理解底层才能用好上层
- "Dapper 不能用存储过程" —— 错。
commandType: CommandType.StoredProcedure即可
总结:对于 90% 的业务场景,Dapper 是更优选择;剩下 10% 涉及底层驱动、极致性能或教学目的,用原生 ADO.NET。 两者并不对立,Dapper 是 ADO.NET 的增强而非替代。

浙公网安备 33010602011771号