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.OrderDateFROM Users uLEFT 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 是一个非常好的选择。