轻量级数据访问实战在.NET 8中用Dapper.Contrib构建高效CRUD当你在开发一个小型API服务或后台任务时Entity Framework Core可能会让你感到有些过度设计——那些复杂的迁移配置、跟踪变更的开销以及不必要的抽象层对于只需要简单CRUD操作的项目来说确实显得有点沉重。这正是Dapper.Contrib的用武之地它保留了ORM的便利性同时提供了接近原生ADO.NET的性能。1. 为什么选择Dapper.Contrib在.NET生态中数据访问方案的选择往往决定了项目的响应速度和维护成本。Entity Framework Core作为全功能ORM适合复杂业务场景但它的学习曲线和运行时开销在小项目中可能成为负担。Dapper.Contrib在原生Dapper基础上添加了最必要的ORM特性极简配置通过[Table]和[Key]等简单注解完成映射自动CRUD提供Insert、Update、Get等开箱即用的方法零跟踪开销没有变更追踪机制内存占用极小混合模式可以同时使用原生SQL和自动生成语句性能测试对比1000次查询方案耗时(ms)内存占用(MB)EF Core 842085原生Dapper10532Dapper.Contrib11535提示当你的项目满足以下条件时Dapper.Contrib是最佳选择数据模型简单不需要复杂映射主要操作是基础的CRUD对性能敏感特别是高频查询场景2. 快速搭建Dapper.Contrib环境2.1 初始化项目从.NET 8 CLI开始dotnet new webapi -n LightweightApi cd LightweightApi dotnet add package Dapper dotnet add package Dapper.Contrib2.2 配置数据库连接创建灵活的连接工厂public class DbConnectionFactory { private readonly IConfiguration _config; public DbConnectionFactory(IConfiguration config) { _config config; } public IDbConnection CreateConnection() { var connectionString _config.GetConnectionString(Default); var connection new SqlConnection(connectionString); connection.Open(); return connection; } }在Program.cs中注册builder.Services.AddScopedDbConnectionFactory();3. 核心CRUD操作实战3.1 定义数据模型使用最小化的注解配置[Table(Products)] // 显式指定表名 public class Product { [ExplicitKey] // 用于非自增主键 public string Id { get; set; } public string Name { get; set; } [Computed] // 标记不参与写入的字段 public DateTime CachedAt { get; set; } }3.2 实现仓储层创建通用仓储基类public class DapperRepositoryT where T : class { private readonly DbConnectionFactory _factory; public DapperRepository(DbConnectionFactory factory) { _factory factory; } public async TaskT GetByIdAsync(string id) { using var conn _factory.CreateConnection(); return await conn.GetAsyncT(id); } public async Taskint CreateAsync(T entity) { using var conn _factory.CreateConnection(); return await conn.InsertAsync(entity); } // 更多方法... }3.3 高级查询技巧虽然Dapper.Contrib提供了基础CRUD但复杂查询仍需原生SQLpublic class ProductRepository : DapperRepositoryProduct { public async TaskListProduct GetExpensiveProductsAsync(decimal minPrice) { const string sql SELECT * FROM Products WHERE Price minPrice ORDER BY CreatedAt DESC; using var conn _factory.CreateConnection(); return (await conn.QueryAsyncProduct(sql, new { minPrice })).ToList(); } }4. 性能优化与实战技巧4.1 批量操作优化使用Dapper的Execute实现批量插入public async Task BulkInsertAsync(IEnumerableProduct products) { const string sql INSERT INTO Products (Id, Name) VALUES (Id, Name); using var conn _factory.CreateConnection(); using var trans conn.BeginTransaction(); try { await conn.ExecuteAsync(sql, products, trans); trans.Commit(); } catch { trans.Rollback(); throw; } }4.2 多数据库支持Dapper.Contrib兼容多种数据库只需调整连接// SQLite示例 public IDbConnection CreateSqliteConnection() { var conn new SQLiteConnection(Data Sourceapp.db); conn.Open(); SqlMapperExtensions.TableNameMapper (type) type.Name.EndsWith(Entity) ? type.Name[..^6] : type.Name; return conn; }4.3 监控与调优添加简单的性能日志public class TimedRepositoryT : DapperRepositoryT { private readonly ILoggerTimedRepositoryT _logger; public override async TaskT GetByIdAsync(string id) { var sw Stopwatch.StartNew(); var result await base.GetByIdAsync(id); _logger.LogDebug(GetById took {Elapsed}ms, sw.ElapsedMilliseconds); return result; } }在最近的一个物联网数据采集项目中我们使用Dapper.Contrib处理每秒300的写入请求平均延迟保持在15ms以下。关键是将业务拆分为多个小型仓储类每个类只关注特定的表操作配合适当的批量处理策略。