Dapper 简介
Dapper 是一个轻量级的 ORM(对象关系映射)库,由 Stack Overflow 团队开发并维护。它旨在提供高效的数据库访问,同时保持代码的简洁性和性能。Dapper 的核心功能是将 SQL 查询结果自动映射到 C# 对象,而不需要编写大量的手动映射代码。
为什么选择 Dapper?
- 轻量级:Dapper 只是一个小型的库,不会像 Entity Framework 或 NHibernate 那样引入大量依赖或复杂的配置。
- 高性能:Dapper 的性能非常接近直接使用 ADO.NET,因为它只做了一层简单的封装,没有额外的开销。
- 灵活性:Dapper 允许你编写原始 SQL 查询,提供了对数据库的完全控制,同时也支持参数化查询和多结果集处理。
- 简单易用:Dapper 的 API 非常简单,学习曲线低,适合快速开发和原型设计。
安装 Dapper
你可以通过 NuGet 包管理器安装 Dapper。在 Visual Studio 中,打开包管理器控制台并运行以下命令:
dotnet add package Dapper
或者,在项目文件中添加以下依赖项:
<PackageReference Include="Dapper" Version="2.0.123" />
基本用法
1. 查询单个对象
假设你有一个 User
类,并且你想从数据库中查询单个用户:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
using (var connection = new SqliteConnection(_connectionString))
{
var user = await connection.QueryFirstOrDefaultAsync<User>("SELECT * FROM Users WHERE Id = @Id", new { Id = 1 });
Console.WriteLine($"User: {user.Name}, Email: {user.Email}");
}
QueryFirstOrDefaultAsync<T>
:执行查询并返回第一个匹配的结果,如果没有任何结果则返回默认值(null
或default(T)
)。@Id
:参数化查询,防止 SQL 注入攻击。
2. 查询多个对象
如果你想查询多个用户,可以使用 QueryAsync<T>
:
using (var connection = new SqliteConnection(_connectionString))
{
var users = await connection.QueryAsync<User>("SELECT * FROM Users");
foreach (var user in users)
{
Console.WriteLine($"User: {user.Name}, Email: {user.Email}");
}
}
QueryAsync<T>
:执行查询并返回一个IEnumerable<T>
,表示查询结果的集合。
3. 插入数据
Dapper 也支持插入、更新和删除操作。你可以使用 ExecuteAsync
方法来执行这些操作:
using (var connection = new SqliteConnection(_connectionString))
{
var newUser = new User { Name = "John Doe", Email = "john.doe@example.com" };
var rowsAffected = await connection.ExecuteAsync("INSERT INTO Users (Name, Email) VALUES (@Name, @Email)", newUser);
Console.WriteLine($"Rows affected: {rowsAffected}");
}
ExecuteAsync
:执行非查询语句(如INSERT
、UPDATE
、DELETE
),并返回受影响的行数。
4. 更新数据
更新操作与插入类似,只需提供要更新的条件:
using (var connection = new SqliteConnection(_connectionString))
{
var updatedUser = new { Id = 1, Name = "Jane Doe", Email = "jane.doe@example.com" };
var rowsAffected = await connection.ExecuteAsync("UPDATE Users SET Name = @Name, Email = @Email WHERE Id = @Id", updatedUser);
Console.WriteLine($"Rows affected: {rowsAffected}");
}
5. 删除数据
删除操作也非常简单:
using (var connection = new SqliteConnection(_connectionString))
{
var rowsAffected = await connection.ExecuteAsync("DELETE FROM Users WHERE Id = @Id", new { Id = 1 });
Console.WriteLine($"Rows affected: {rowsAffected}");
}
6. 多结果集处理
Dapper 支持多结果集查询,即一次查询返回多个表的数据。你可以使用 QueryMultiple
方法来处理这种情况:
using (var connection = new SqliteConnection(_connectionString))
{
using (var multi = await connection.QueryMultipleAsync("SELECT * FROM Users; SELECT * FROM Orders"))
{
var users = await multi.ReadAsync<User>();
var orders = await multi.ReadAsync<Order>();
foreach (var user in users)
{
Console.WriteLine($"User: {user.Name}");
}
foreach (var order in orders)
{
Console.WriteLine($"Order: {order.OrderId}");
}
}
}
QueryMultipleAsync
:执行多结果集查询,并返回一个SqlMapper.GridReader
对象,可以通过ReadAsync<T>
方法逐个读取每个结果集。
高级功能
1. 自动映射复杂对象
Dapper 支持自动映射复杂对象,包括嵌套对象和多个表的联合查询。例如,假设你有 User
和 Order
两个表,并且你想查询每个用户的订单信息:
public class UserWithOrders
{
public int Id { get; set; }
public string Name { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int OrderId { get; set; }
public DateTime OrderDate { get; set; }
}
using (var connection = new SqliteConnection(_connectionString))
{
var sql = @"
SELECT u.Id, u.Name, o.OrderId, o.OrderDate
FROM Users u
LEFT JOIN Orders o ON u.Id = o.UserId";
var result = await connection.QueryAsync<User, Order, UserWithOrders>(
sql,
(user, order) =>
{
var userWithOrders = new UserWithOrders
{
Id = user.Id,
Name = user.Name,
Orders = new List<Order>()
};
if (order != null)
{
userWithOrders.Orders.Add(order);
}
return userWithOrders;
},
splitOn: "OrderId"
);
// 使用 LINQ 进行分组
var usersWithOrders = result.GroupBy(x => x.Id)
.Select(g => new UserWithOrders
{
Id = g.Key,
Name = g.First().Name,
Orders = g.SelectMany(x => x.Orders).ToList()
})
.ToList();
foreach (var user in usersWithOrders)
{
Console.WriteLine($"User: {user.Name}");
foreach (var order in user.Orders)
{
Console.WriteLine($" Order: {order.OrderId}, Date: {order.OrderDate}");
}
}
}
QueryAsync<T1, T2, TReturn>
:用于映射多个表的联合查询。splitOn
参数指定了如何区分不同的表(通常是主键列)。GroupBy
:用于将多个结果集分组为单个对象。
2. 事务支持
Dapper 也支持事务操作。你可以使用 BeginTransaction
方法来启动一个事务,并在提交或回滚时进行控制:
using (var connection = new SqliteConnection(_connectionString))
{
using (var transaction = await connection.BeginTransactionAsync())
{
try
{
// 执行插入操作
await connection.ExecuteAsync("INSERT INTO Users (Name, Email) VALUES (@Name, @Email)", new { Name = "John Doe", Email = "john.doe@example.com" }, transaction);
// 执行另一个插入操作
await connection.ExecuteAsync("INSERT INTO Orders (UserId, OrderDate) VALUES (@UserId, @OrderDate)", new { UserId = 1, OrderDate = DateTime.Now }, transaction);
// 提交事务
await transaction.CommitAsync();
}
catch (Exception ex)
{
// 回滚事务
await transaction.RollbackAsync();
throw;
}
}
}
性能优化
Dapper 的性能非常接近原生 ADO.NET,因为它只做了一层简单的封装。为了进一步提高性能,你可以考虑以下几点:
- 使用缓存:Dapper 提供了内置的查询计划缓存机制,可以在多次执行相同的查询时提高性能。
- 批量操作:对于批量插入或更新操作,可以使用
ExecuteBatch
或BulkCopy
等方法来减少数据库往返次数。 - 异步编程:尽量使用异步方法(如
QueryAsync
、ExecuteAsync
)来避免阻塞主线程,特别是在 Web 应用程序中。
总结
Dapper 是一个非常强大且灵活的轻量级 ORM 库,适合需要高效数据库访问的应用程序。它提供了简单易用的 API,支持多种数据库类型(如 SQLite、SQL Server、MySQL 等),并且能够很好地与现有的 ADO.NET 代码集成。如果你希望在保持高性能的同时简化数据库操作,Dapper 是一个非常好的选择。