dapper-dot-net异步编程:Async方法族与高性能并发处理
【免费下载链接】Dapper 项目地址: https://gitcode.com/gh_mirrors/dappe/dapper-dot-net
在现代应用开发中,数据库操作的性能直接影响用户体验。传统同步数据库调用会阻塞线程,导致资源浪费和响应延迟。Dapper作为轻量级ORM(对象关系映射)工具,通过其Async方法族提供了高效的异步数据库操作能力,帮助开发者构建高性能并发应用。本文将详细介绍Dapper的异步编程模型、核心API使用方法及性能优化技巧,让你轻松掌握异步数据库操作的精髓。
Dapper异步编程基础
Dapper的异步API位于SqlMapper.Async.cs文件中,通过扩展方法为IDbConnection接口添加了一系列异步操作。这些方法遵循.NET异步编程规范,返回Task或Task<T>类型,支持async/await语法,使开发者能够以简洁的方式编写非阻塞数据库代码。
核心异步方法分类
Dapper的异步方法主要分为以下几类:
- 查询方法:用于执行查询并返回结果集,如
QueryAsync、QueryFirstAsync、QuerySingleAsync等 - 执行方法:用于执行插入、更新、删除等操作,如
ExecuteAsync - 多结果集方法:用于处理多个结果集,如
QueryMultipleAsync
这些方法都支持通过CommandDefinition参数配置超时时间、事务、取消令牌等高级选项,提供了灵活的异步操作控制能力。
常用Async方法实战
1. 基础查询:QueryAsync
QueryAsync是最常用的异步查询方法,用于执行SQL查询并返回结果集。它支持泛型和动态类型两种返回方式,满足不同场景需求。
泛型查询示例:
// 泛型查询,返回指定类型的结果集
var users = await connection.QueryAsync<User>(
"SELECT Id, Name FROM Users WHERE Age > @Age",
new { Age = 18 }
).ConfigureAwait(false);
foreach (var user in users)
{
Console.WriteLine($"Id: {user.Id}, Name: {user.Name}");
}
动态类型查询示例:
// 动态类型查询,适合临时数据处理
var users = await connection.QueryAsync(
"SELECT Id, Name FROM Users WHERE Age > @Age",
new { Age = 18 }
).ConfigureAwait(false);
foreach (var user in users)
{
Console.WriteLine($"Id: {user.Id}, Name: {user.Name}");
}
相关源码:Dapper/SqlMapper.Async.cs
2. 单结果查询:QueryFirstAsync与QuerySingleAsync
当只需要获取单个结果时,可以使用QueryFirstAsync或QuerySingleAsync方法,它们的区别在于:
QueryFirstAsync:返回结果集中的第一条记录,不检查记录数量QuerySingleAsync:返回结果集中的唯一一条记录,如果记录数量不为1则抛出异常
示例:
// 获取第一条记录
var firstUser = await connection.QueryFirstAsync<User>(
"SELECT Id, Name FROM Users ORDER BY Id"
).ConfigureAwait(false);
// 获取唯一记录,确保查询结果只有一条
var singleUser = await connection.QuerySingleAsync<User>(
"SELECT Id, Name FROM Users WHERE Id = @Id",
new { Id = 1 }
).ConfigureAwait(false);
测试用例参考:tests/Dapper.Tests/AsyncTests.cs中的TestBasicStringUsageQueryFirstAsync方法
3. 执行命令:ExecuteAsync
ExecuteAsync用于执行插入、更新、删除等SQL命令,返回受影响的行数。
插入数据示例:
var affectedRows = await connection.ExecuteAsync(
"INSERT INTO Users (Name, Age) VALUES (@Name, @Age)",
new { Name = "John Doe", Age = 30 }
).ConfigureAwait(false);
Console.WriteLine($"插入了 {affectedRows} 条记录");
批量插入示例:
var users = new[] {
new { Name = "Alice", Age = 25 },
new { Name = "Bob", Age = 30 },
new { Name = "Charlie", Age = 35 }
};
var affectedRows = await connection.ExecuteAsync(
"INSERT INTO Users (Name, Age) VALUES (@Name, @Age)",
users
).ConfigureAwait(false);
Console.WriteLine($"批量插入了 {affectedRows} 条记录");
相关测试:tests/Dapper.Tests/AsyncTests.cs中的TestExecuteAsync方法
4. 多结果集处理:QueryMultipleAsync
当需要从单个查询中获取多个结果集时,可以使用QueryMultipleAsync方法,配合ReadAsync方法依次读取每个结果集。
示例:
using (var multi = await connection.QueryMultipleAsync(
"SELECT Id, Name FROM Users; SELECT Id, Title FROM Posts"
).ConfigureAwait(false))
{
var users = await multi.ReadAsync<User>().ConfigureAwait(false);
var posts = await multi.ReadAsync<Post>().ConfigureAwait(false);
Console.WriteLine("用户列表:");
foreach (var user in users)
{
Console.WriteLine($" {user.Id}: {user.Name}");
}
Console.WriteLine("文章列表:");
foreach (var post in posts)
{
Console.WriteLine($" {post.Id}: {post.Title}");
}
}
高级特性与性能优化
1. 取消异步操作:CancellationToken
Dapper的异步方法支持通过CommandDefinition参数传递CancellationToken,用于取消长时间运行的数据库操作。
示例:
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); // 10秒超时
try
{
var users = await connection.QueryAsync<User>(
new CommandDefinition(
"SELECT Id, Name FROM Users",
cancellationToken: cts.Token
)
).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
Console.WriteLine("查询已取消");
}
测试用例:tests/Dapper.Tests/AsyncTests.cs中的TestBasicStringUsageUnbufferedAsync_Cancellation方法
2. 非缓冲查询:CommandFlags.None
默认情况下,Dapper会将查询结果全部加载到内存中(缓冲查询)。对于大型结果集,可以使用非缓冲查询(流式查询)来减少内存占用。
示例:
// 使用非缓冲查询处理大型结果集
var users = await connection.QueryAsync<User>(
new CommandDefinition(
"SELECT Id, Name FROM LargeTable",
flags: CommandFlags.None // 禁用缓冲
)
).ConfigureAwait(false);
foreach (var user in users)
{
// 处理每个用户,一次只加载少量数据到内存
}
3. 管道化查询:CommandFlags.Pipelined
在启用MARS(Multiple Active Result Sets)的情况下,使用CommandFlags.Pipelined可以进一步提高多个异步查询的性能。
示例:
var ids = Enumerable.Range(1, 1000).Select(id => new { Id = id }).ToArray();
// 使用管道化查询提高性能
await connection.ExecuteAsync(
new CommandDefinition(
"SELECT @Id",
ids,
flags: CommandFlags.Pipelined // 启用管道化
)
).ConfigureAwait(false);
性能测试参考:tests/Dapper.Tests/AsyncTests.cs中的RunSequentialVersusParallelAsync方法
异步事务处理
Dapper支持异步事务,确保多个数据库操作的原子性。
示例:
using (var transaction = await connection.BeginTransactionAsync().ConfigureAwait(false))
{
try
{
// 执行多个异步操作
await connection.ExecuteAsync(
"INSERT INTO Users (Name) VALUES (@Name)",
new { Name = "Transaction Test" },
transaction: transaction
).ConfigureAwait(false);
await connection.ExecuteAsync(
"INSERT INTO Logs (Message) VALUES (@Message)",
new { Message = "User created" },
transaction: transaction
).ConfigureAwait(false);
// 提交事务
transaction.Commit();
}
catch
{
// 回滚事务
transaction.Rollback();
throw;
}
}
常见问题与最佳实践
1. 避免死锁:正确使用ConfigureAwait(false)
在异步代码中,使用ConfigureAwait(false)可以避免上下文切换导致的死锁,特别是在非UI线程中。Dapper的所有异步方法都应该使用ConfigureAwait(false),除非需要返回到原始上下文。
推荐写法:
// 正确使用ConfigureAwait(false)
var result = await connection.QueryAsync<User>(sql).ConfigureAwait(false);
2. 连接管理:确保连接正确释放
虽然Dapper简化了数据库操作,但仍需确保数据库连接正确释放。推荐使用using语句管理连接生命周期。
正确写法:
using (var connection = new SqlConnection(connectionString))
{
await connection.OpenAsync().ConfigureAwait(false);
// 执行数据库操作
var users = await connection.QueryAsync<User>("SELECT Id, Name FROM Users").ConfigureAwait(false);
}
3. 参数化查询:防止SQL注入
始终使用参数化查询,避免直接拼接SQL字符串,以防止SQL注入攻击。Dapper默认支持参数化查询,只需传递匿名对象作为参数即可。
错误写法:
// 危险:容易导致SQL注入
var sql = $"SELECT * FROM Users WHERE Name = '{name}'";
var users = await connection.QueryAsync<User>(sql).ConfigureAwait(false);
正确写法:
// 安全:使用参数化查询
var sql = "SELECT * FROM Users WHERE Name = @Name";
var users = await connection.QueryAsync<User>(sql, new { Name = name }).ConfigureAwait(false);
总结与展望
Dapper的Async方法族为开发者提供了高效、灵活的异步数据库操作能力,通过合理使用这些方法,可以显著提高应用程序的并发性能和响应速度。本文介绍了Dapper异步编程的核心方法、高级特性和最佳实践,涵盖了从基础查询到事务处理的各个方面。
随着.NET平台的不断发展,Dapper也在持续优化和更新,未来可能会引入更多异步相关的性能优化和新特性。建议开发者关注官方仓库和文档,及时了解最新动态。
官方文档:docs/index.md
如果你觉得本文对你有帮助,请点赞、收藏并关注,以便获取更多Dapper使用技巧和最佳实践。下期我们将深入探讨Dapper的类型处理和自定义映射功能,敬请期待!
【免费下载链接】Dapper 项目地址: https://gitcode.com/gh_mirrors/dappe/dapper-dot-net
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




