前言
在 .NET 应用开发中,数据访问层的设计与实现对整体性能和可维护性具有决定性影响。传统的 ADO.NET 虽然灵活但代码冗长,而全功能 ORM(如 Entity Framework)则可能带来性能损耗和学习成本。Dapper 作为一款轻量级 ORM,凭借其极低的开销和简洁的 API 接口,成为众多开发者在需要直接控制 SQL 并兼顾性能的场景下的首选。
Dapper 简介
Dapper 是一个基于 ADO.NET 构建的微型对象关系映射器(Micro-ORM),其设计目标是为开发者提供一种既保留原生 SQL 控制力,又具备类型安全和便捷性的数据库访问方式。
不同于 Entity Framework 等重型 ORM,Dapper 不依赖复杂的模型生成机制或 LINQ 查询翻译,而是直接通过 SQL 字符串执行查询,并自动将结果映射到强类型对象或动态类型中。这种“少即是多”的设计理念使其在性能方面表现优异,同时保持了高度灵活性。
Dapper 支持主流数据库平台,包括 SQL Server、MySQL、PostgreSQL、SQLite 等,并可通过参数化查询防止 SQL 注入攻击,确保安全性。
安装与环境配置
Dapper 可通过 NuGet 快速集成到 .NET 项目中,支持多种运行时环境和开发框架。
使用 NuGet 安装
推荐使用 NuGet 包管理器进行安装:
dotnet add package Dapper
或者在 Visual Studio 中搜索 Dapper
并安装。
初始化数据库连接
Dapper 不管理数据库连接生命周期,因此你需要自行创建并维护 IDbConnection
实例。以下是一个典型的 SQL Server 连接示例:
using System.Data.SqlClient;
var connectionString = "Server=your_server;Database=your_db;User Id=sa;Password=your_pwd;";
using var connection = new SqlConnection(connectionString);
connection.Open();
对于 MySQL 或 PostgreSQL,只需更换对应的 ADO.NET 提供者即可,例如 MySqlConnection
或 NpgsqlConnection
。
核心功能与使用方式
Dapper 提供了一系列扩展方法,使数据库操作更加简洁且类型安全。
查询单个对象
使用 QueryFirstOrDefault<T>
方法可以查询返回单个对象:
var user = connection.QueryFirstOrDefault<User>(
"SELECT * FROM Users WHERE Id = @Id",
new { Id = 1 });
上述代码中,@Id
是参数化查询的占位符,传入的对象属性名需与占位符名称一致。
查询多个对象
获取多个记录时,可以使用 Query<T>
方法:
var users = connection.Query<User>("SELECT * FROM Users").ToList();
该方法会自动将每行数据映射到 User
类实例。
动态类型查询
如果你希望不定义实体类也能处理结果,可以使用 dynamic
类型:
var result = connection.Query("SELECT Name, Age FROM Users").ToList();
foreach (var item in result)
{
Console.WriteLine($"Name: {item.Name}, Age: {item.Age}");
}
插入数据
执行插入操作时,可以使用 Execute
方法:
var affectedRows = connection.Execute(
"INSERT INTO Users (Name, Email) VALUES (@Name, @Email)",
new { Name = "Tom", Email = "tom@example.com" });
返回值表示受影响的行数。
更新与删除
更新和删除操作同样使用 Execute
方法:
// 更新
var rowsUpdated = connection.Execute(
"UPDATE Users SET Email = @Email WHERE Id = @Id",
new { Email = "new_email@example.com", Id = 1 });
// 删除
var rowsDeleted = connection.Execute(
"DELETE FROM Users WHERE Id = @Id",
new { Id = 1 });
多结果集查询
Dapper 支持一次查询返回多个结果集,适用于复杂报表或聚合查询场景:
using var multi = connection.QueryMultiple("SELECT * FROM Users; SELECT * FROM Roles");
var users = multi.Read<User>().ToList();
var roles = multi.Read<Role>().ToList();
存储过程调用
调用存储过程也非常简单,只需指定命令类型:
var user = connection.QueryFirstOrDefault<User>(
"GetUserById",
new { Id = 1 },
commandType: CommandType.StoredProcedure);
事务处理
Dapper 支持事务操作,确保多个数据库操作的原子性:
using var transaction = connection.BeginTransaction();
try
{
connection.Execute("INSERT INTO Logs (Message) VALUES ('Start')", transaction: transaction);
connection.Execute("UPDATE Users SET Status = 1 WHERE Id = 1", transaction: transaction);
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
映射规则与自定义处理
Dapper 默认根据字段名匹配实体属性名,若存在不一致情况,可以通过别名或自定义映射器进行调整。
自定义字段映射
你可以使用 SqlMapper.TypeMap
来注册自定义映射逻辑:
SqlMapper.SetTypeMap(
typeof(User),
new CustomPropertyTypeMap(
typeof(User),
(type, columnName) =>
type.GetProperties().FirstOrDefault(prop => prop.Name.ToLower() == columnName.ToLower())));
复杂对象嵌套映射
当查询结果包含多个表的数据时,可以使用多映射函数来处理关联对象:
var sql = "SELECT * FROM Orders o JOIN Users u ON o.UserId = u.Id";
var orders = connection.Query<Order, User, Order>(
sql,
(order, user) =>
{
order.User = user;
return order;
},
splitOn: "Id"
).ToList();
参数化查询与安全机制
Dapper 强烈推荐使用参数化查询,以防止 SQL 注入攻击。以下是一个安全写法示例:
var user = connection.Query<User>(
"SELECT * FROM Users WHERE Name = @Name",
new { Name = userInput });
避免拼接字符串构造 SQL:
// ❌ 不推荐
string query = $"SELECT * FROM Users WHERE Name = '{userInput}'";
错误处理与日志输出
虽然 Dapper 本身不提供日志功能,但你可以结合日志框架(如 Serilog、NLog 或 Microsoft.Extensions.Logging)记录执行的 SQL 和异常信息。
例如,在捕获异常时输出 SQL:
try
{
// 执行数据库操作
}
catch (Exception ex)
{
logger.LogError(ex, "执行 SQL 出错:{sql}", sql);
throw;
}
多数据库兼容性
Dapper 本身不绑定任何特定数据库引擎,只要提供符合 ADO.NET 规范的驱动,即可在不同数据库平台上运行。常见的适配器包括:
- SQL Server:
System.Data.SqlClient
- MySQL:
MySql.Data.MySqlClient
或MySqlConnector
- PostgreSQL:
Npgsql
- SQLite:
Microsoft.Data.Sqlite
你可以在不同数据库之间切换,只需修改连接字符串和提供对应数据库的连接对象。