Dapper作为.NET生态中广受欢迎的轻量级ORM(对象关系映射)工具,以其卓越的性能和简洁的API著称。本文将全面介绍Dapper的核心功能、高效查询技巧以及实际应用场景,帮助开发者充分发挥其性能优势。
一、Dapper基础入门
1.1 Dapper核心优势
- 超高性能:接近原生ADO.NET的执行效率
- 简洁API:扩展了
IDbConnection
接口 - 最小开销:没有复杂的变更跟踪机制
- 灵活映射:支持对象与关系数据的多种映射方式
1.2 安装与基本配置
通过NuGet安装Dapper:
Install-Package Dapper
基础使用示例:
using System.Data.SqlClient;
using Dapper;
// 创建数据库连接
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
// 执行查询
var products = connection.Query<Product>("SELECT * FROM Products WHERE Price > @price",
new { price = 100 });
}
二、基本查询操作
2.1 简单查询与参数化
// 查询单个对象
var product = connection.QueryFirstOrDefault<Product>(
"SELECT * FROM Products WHERE Id = @id",
new { id = 1 });
// 查询列表
var expensiveProducts = connection.Query<Product>(
"SELECT * FROM Products WHERE Price > @minPrice ORDER BY Price DESC",
new { minPrice = 500 });
// 多参数查询
var filteredProducts = connection.Query<Product>(
"SELECT * FROM Products WHERE Price BETWEEN @minPrice AND @maxPrice AND CategoryId = @categoryId",
new {
minPrice = 100,
maxPrice = 1000,
categoryId = 5
});
2.2 执行非查询操作
// 插入数据
var affectedRows = connection.Execute(
"INSERT INTO Products (Name, Price) VALUES (@name, @price)",
new { name = "无线耳机", price = 299.99m });
// 批量插入
var products = new List<Product>
{
new Product { Name = "键盘", Price = 199.99m },
new Product { Name = "鼠标", Price = 99.99m }
};
var totalInserted = connection.Execute(
"INSERT INTO Products (Name, Price) VALUES (@Name, @Price)",
products);
三、高级查询技巧
3.1 多映射(一对多关系)
var sql = @"
SELECT p.*, c.*
FROM Products p
INNER JOIN Categories c ON p.CategoryId = c.Id
WHERE p.Price > @minPrice";
var products = connection.Query<Product, Category, Product>(
sql,
(product, category) =>
{
product.Category = category;
return product;
},
new { minPrice = 100 },
splitOn: "Id"); // 指定分割列
// 结果处理
foreach (var p in products)
{
Console.WriteLine($"{p.Name} - {p.Category.Name}");
}
3.2 多结果集查询
var sql = @"
SELECT * FROM Products WHERE Price > 500;
SELECT * FROM Categories;";
using (var multi = connection.QueryMultiple(sql))
{
var products = multi.Read<Product>().ToList();
var categories = multi.Read<Category>().ToList();
// 处理结果...
}
3.3 存储过程调用
var parameters = new DynamicParameters();
parameters.Add("@categoryId", 5);
parameters.Add("@totalCount", dbType: DbType.Int32, direction: ParameterDirection.Output);
var products = connection.Query<Product>(
"GetProductsByCategory",
parameters,
commandType: CommandType.StoredProcedure);
int totalCount = parameters.Get<int>("@totalCount");
四、性能优化策略
4.1 缓冲与无缓冲查询
// 缓冲查询(默认)- 所有结果读入内存
var products = connection.Query<Product>("SELECT * FROM Products");
// 无缓冲查询 - 流式处理大数据
var products = connection.QueryUnbuffered<Product>("SELECT * FROM LargeProductsTable");
foreach (var product in products) // 一次处理一行
{
ProcessProduct(product);
}
4.2 批量操作优化
// 使用Table-Valued参数进行高效批量操作
DataTable productTable = new DataTable();
productTable.Columns.Add("Name", typeof(string));
productTable.Columns.Add("Price", typeof(decimal));
// 添加数据行...
foreach (var p in productsToInsert)
{
productTable.Rows.Add(p.Name, p.Price);
}
var param = new { Products = productTable.AsTableValuedParameter("dbo.ProductTableType") };
connection.Execute("INSERT INTO Products SELECT * FROM @Products", param);
4.3 自定义类型处理
// 自定义类型处理器(处理特殊类型如JSON)
SqlMapper.AddTypeHandler(new JsonTypeHandler<ProductDetails>());
public class JsonTypeHandler<T> : SqlMapper.TypeHandler<T>
{
public override T Parse(object value)
{
return JsonSerializer.Deserialize<T>(value.ToString());
}
public override void SetValue(IDbDataParameter parameter, T value)
{
parameter.Value = JsonSerializer.Serialize(value);
}
}
// 使用
var product = connection.Query<Product>("SELECT * FROM ProductsWithDetails");
五、实际应用模式
5.1 仓储模式实现
public interface IProductRepository
{
Task<Product> GetByIdAsync(int id);
Task<IEnumerable<Product>> GetByCategoryAsync(int categoryId);
Task<int> AddAsync(Product product);
}
public class DapperProductRepository : IProductRepository
{
private readonly IDbConnectionFactory _connectionFactory;
public DapperProductRepository(IDbConnectionFactory connectionFactory)
{
_connectionFactory = connectionFactory;
}
public async Task<Product> GetByIdAsync(int id)
{
using (var connection = _connectionFactory.CreateConnection())
{
return await connection.QueryFirstOrDefaultAsync<Product>(
"SELECT * FROM Products WHERE Id = @id",
new { id });
}
}
// 其他方法实现...
}
5.2 与DI容器集成
// 在Startup.cs中配置
services.AddScoped<IDbConnectionFactory>(provider =>
new SqlConnectionFactory(Configuration.GetConnectionString("Default")));
services.AddScoped<IProductRepository, DapperProductRepository>();
// SQL连接工厂实现
public class SqlConnectionFactory : IDbConnectionFactory
{
private readonly string _connectionString;
public SqlConnectionFactory(string connectionString)
{
_connectionString = connectionString;
}
public IDbConnection CreateConnection()
{
var connection = new SqlConnection(_connectionString);
connection.Open();
return connection;
}
}
六、Dapper扩展与生态系统
6.1 Dapper.Contrib
// 安装扩展包
Install-Package Dapper.Contrib
// 简化CRUD操作
using Dapper.Contrib.Extensions;
// 获取所有产品
var products = connection.GetAll<Product>().ToList();
// 插入新记录
var newId = connection.Insert(new Product { Name = "新品", Price = 199.99m });
// 更新记录
product.Price = 299.99m;
connection.Update(product);
// 删除记录
connection.Delete(product);
6.2 Dapper Plus(批量操作)
// 安装扩展包
Install-Package Z.Dapper.Plus
// 批量插入
connection.BulkInsert(products);
// 批量更新
connection.BulkUpdate(products);
// 批量删除
connection.BulkDelete(products);
七、常见问题解决方案
7.1 处理复杂查询结果
// 动态结果处理
var results = connection.Query("SELECT p.*, c.Name as CategoryName FROM Products p JOIN Categories c ON p.CategoryId = c.Id");
foreach (dynamic row in results)
{
Console.WriteLine($"{row.Name} - {row.CategoryName}");
}
// 多结果映射到不同对象
var queryResult = connection.Query<Product, Category, Product>(
"SELECT p.*, c.* FROM Products p JOIN Categories c ON p.CategoryId = c.Id",
(product, category) =>
{
product.Category = category;
return product;
},
splitOn: "Id");
7.2 性能监控与调优
// 安装MiniProfiler
Install-Package MiniProfiler.AspNetCore
Install-Package MiniProfiler.Providers.SqlServer
// 配置
services.AddMiniProfiler(options =>
{
options.RouteBasePath = "/profiler";
options.SqlFormatter = new StackExchange.Profiling.SqlFormatters.InlineFormatter();
}).AddEntityFramework();
// 在查询中使用
using (MiniProfiler.Current.Step("查询产品数据"))
{
var products = connection.Query<Product>("SELECT * FROM Products WHERE Price > @price",
new { price = 100 });
}
八、Dapper最佳实践
- 连接管理:
- 使用
using
语句确保连接及时关闭 - 考虑使用连接池优化性能
- 参数化查询:
- 始终使用参数化查询防止SQL注入
- 对复杂参数使用
DynamicParameters
- 性能平衡:
- 简单查询使用Dapper
- 复杂领域模型考虑结合EF Core
- 代码组织:
- 将SQL查询集中管理
- 使用仓储模式抽象数据访问
// SQL集中管理示例
public static class ProductSql
{
public static string GetById => "SELECT * FROM Products WHERE Id = @id";
public static string GetByCategory => """
SELECT p.*, c.Name as CategoryName
FROM Products p
JOIN Categories c ON p.CategoryId = c.Id
WHERE p.CategoryId = @categoryId
""";
}
// 在仓储中使用
public async Task<Product> GetByIdAsync(int id)
{
using (var connection = _connectionFactory.CreateConnection())
{
return await connection.QueryFirstOrDefaultAsync<Product>(
ProductSql.GetById,
new { id });
}
}
结语
Dapper作为.NET平台上的高性能微ORM,在需要极致性能的场景下表现出色。通过本指南,您应该已经掌握:
- Dapper核心功能:基本CRUD操作、参数化查询
- 高级技巧:多映射、多结果集、存储过程调用
- 性能优化:批量操作、缓冲控制、类型处理
- 架构模式:仓储模式实现、DI集成
实际项目中使用建议:
- 在简单到中等复杂度查询中优先使用Dapper
- 对性能关键路径进行基准测试
- 合理组织SQL查询,保持可维护性
- 结合Dapper扩展增强功能
Dapper与Entity Framework Core不是非此即彼的选择,根据场景合理搭配使用,可以构建出既高效又可维护的数据访问层。