尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Dapper ORM实现高效SQL CRUD操作指南
1. Dapper与原生SQL CRUD操作概述在.NET生态系统中Dapper作为一款轻量级ORM工具以其卓越的性能和简洁的API设计赢得了开发者的青睐。与Entity Framework等全功能ORM不同Dapper采用微ORM的架构理念它不尝试抽象掉SQL本身而是提供高效的对象映射机制让开发者可以完全掌控SQL语句的编写。原生SQL CRUDCreate, Read, Update, Delete是数据库操作的基础Dapper通过扩展方法将这些操作变得异常简单。以下是Dapper的核心优势近乎原生ADO.NET的性能表现极低的学习曲线和简洁的API设计完整的SQL控制权灵活的参数化查询支持强大的多映射能力2. 环境准备与基础配置2.1 项目初始化与依赖安装首先创建一个新的.NET Core控制台应用程序并添加必要的NuGet包dotnet new console -n DapperDemo cd DapperDemo dotnet add package Dapper dotnet add package Microsoft.Data.SqlClient对于不同的数据库类型可以选择对应的ADO.NET提供程序SQL Server: Microsoft.Data.SqlClientMySQL: MySql.DataPostgreSQL: NpgsqlSQLite: Microsoft.Data.Sqlite2.2 数据库连接配置在appsettings.json中配置数据库连接字符串{ ConnectionStrings: { DefaultConnection: Server(localdb)\\mssqllocaldb;DatabaseDapperDemoDB;Trusted_ConnectionTrue; } }创建数据库连接辅助类using System.Data; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Configuration; public class DbContext { private readonly IConfiguration _config; public DbContext(IConfiguration config) { _config config; } public IDbConnection CreateConnection() new SqlConnection(_config.GetConnectionString(DefaultConnection)); }3. 基础CRUD操作实现3.1 实体类定义首先定义我们的领域模型以Product类为例public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } public int Stock { get; set; } public DateTime CreatedDate { get; set; } DateTime.UtcNow; }3.2 创建(Create)操作使用Dapper执行INSERT操作public class ProductRepository { private readonly DbContext _dbContext; public ProductRepository(DbContext dbContext) { _dbContext dbContext; } public async Taskint CreateAsync(Product product) { using var connection _dbContext.CreateConnection(); var sql INSERT INTO Products (Name, Price, Stock, CreatedDate) VALUES (Name, Price, Stock, CreatedDate); SELECT CAST(SCOPE_IDENTITY() as int); return await connection.ExecuteScalarAsyncint(sql, product); } }关键点说明ExecuteScalarAsync用于获取插入后生成的ID参数通过匿名对象或实体类本身传递SQL中的参数名必须与对象属性名匹配3.3 查询(Read)操作实现各种查询场景// 获取单个实体 public async TaskProduct GetByIdAsync(int id) { using var connection _dbContext.CreateConnection(); var sql SELECT * FROM Products WHERE Id Id; return await connection.QueryFirstOrDefaultAsyncProduct(sql, new { Id id }); } // 获取列表 public async TaskIEnumerableProduct GetAllAsync() { using var connection _dbContext.CreateConnection(); return await connection.QueryAsyncProduct(SELECT * FROM Products ORDER BY Id); } // 带条件的查询 public async TaskIEnumerableProduct GetByPriceRangeAsync(decimal minPrice, decimal maxPrice) { using var connection _dbContext.CreateConnection(); var sql SELECT * FROM Products WHERE Price BETWEEN MinPrice AND MaxPrice; return await connection.QueryAsyncProduct(sql, new { MinPrice minPrice, MaxPrice maxPrice }); }3.4 更新(Update)操作实现数据更新public async Taskbool UpdateAsync(Product product) { using var connection _dbContext.CreateConnection(); var sql UPDATE Products SET Name Name, Price Price, Stock Stock WHERE Id Id; var affectedRows await connection.ExecuteAsync(sql, product); return affectedRows 0; }3.5 删除(Delete)操作实现数据删除public async Taskbool DeleteAsync(int id) { using var connection _dbContext.CreateConnection(); var sql DELETE FROM Products WHERE Id Id; var affectedRows await connection.ExecuteAsync(sql, new { Id id }); return affectedRows 0; }4. 高级查询技巧4.1 多表关联查询Dapper处理复杂查询同样得心应手。假设我们有一个Order实体public class Order { public int Id { get; set; } public DateTime OrderDate { get; set; } public decimal TotalAmount { get; set; } public ListOrderItem Items { get; set; } } public class OrderItem { public int Id { get; set; } public int OrderId { get; set; } public int ProductId { get; set; } public int Quantity { get; set; } public decimal UnitPrice { get; set; } public Product Product { get; set; } }实现多表查询public async TaskOrder GetOrderWithItemsAsync(int orderId) { using var connection _dbContext.CreateConnection(); var sql SELECT o.*, oi.*, p.* FROM Orders o LEFT JOIN OrderItems oi ON o.Id oi.OrderId LEFT JOIN Products p ON oi.ProductId p.Id WHERE o.Id OrderId; var orderDictionary new Dictionaryint, Order(); var result await connection.QueryAsyncOrder, OrderItem, Product, Order( sql, (order, orderItem, product) { if (!orderDictionary.TryGetValue(order.Id, out var existingOrder)) { existingOrder order; existingOrder.Items new ListOrderItem(); orderDictionary.Add(existingOrder.Id, existingOrder); } if (orderItem ! null) { orderItem.Product product; existingOrder.Items.Add(orderItem); } return existingOrder; }, new { OrderId orderId }, splitOn: Id,Id); return orderDictionary.Values.FirstOrDefault(); }4.2 存储过程调用Dapper完美支持存储过程public async TaskIEnumerableProduct GetTopSellingProductsAsync(int count) { using var connection _dbContext.CreateConnection(); return await connection.QueryAsyncProduct( sp_GetTopSellingProducts, new { Count count }, commandType: CommandType.StoredProcedure); }4.3 批量操作使用Dapper的批量操作提高性能public async Taskint BulkInsertProductsAsync(IEnumerableProduct products) { using var connection _dbContext.CreateConnection(); var sql INSERT INTO Products (Name, Price, Stock, CreatedDate) VALUES (Name, Price, Stock, CreatedDate); return await connection.ExecuteAsync(sql, products); }5. 性能优化与最佳实践5.1 连接管理策略正确的连接管理对性能至关重要// 错误做法 - 不要这样使用 public IEnumerableProduct GetProducts() { using var connection new SqlConnection(_connectionString); return connection.QueryProduct(SELECT * FROM Products); } // 正确做法 - 在单个方法调用中完成连接生命周期 public async TaskIEnumerableProduct GetProductsAsync() { await using var connection _dbContext.CreateConnection(); await connection.OpenAsync(); return await connection.QueryAsyncProduct(SELECT * FROM Products); }5.2 参数化查询始终使用参数化查询防止SQL注入// 不安全 - 字符串拼接 var unsafeSql $SELECT * FROM Products WHERE Name {name}; // 安全 - 参数化查询 var safeSql SELECT * FROM Products WHERE Name Name; var products await connection.QueryAsyncProduct(safeSql, new { Name name });5.3 异步操作充分利用Dapper的异步APIpublic async TaskProduct GetProductAsync(int id) { await using var connection _dbContext.CreateConnection(); await connection.OpenAsync(); return await connection.QueryFirstOrDefaultAsyncProduct( SELECT * FROM Products WHERE Id Id, new { Id id }); }5.4 事务处理实现原子性操作public async Taskbool ProcessOrderAsync(Order order) { await using var connection _dbContext.CreateConnection(); await connection.OpenAsync(); using var transaction await connection.BeginTransactionAsync(); try { // 插入订单 var orderSql INSERT INTO Orders (OrderDate, TotalAmount) VALUES (OrderDate, TotalAmount); SELECT CAST(SCOPE_IDENTITY() as int); var orderId await connection.ExecuteScalarAsyncint( orderSql, order, transaction: transaction); // 插入订单项 foreach (var item in order.Items) { item.OrderId orderId; var itemSql INSERT INTO OrderItems (OrderId, ProductId, Quantity, UnitPrice) VALUES (OrderId, ProductId, Quantity, UnitPrice); await connection.ExecuteAsync( itemSql, item, transaction: transaction); } await transaction.CommitAsync(); return true; } catch { await transaction.RollbackAsync(); throw; } }6. 常见问题与解决方案6.1 列名与属性名不匹配当数据库列名与C#属性名不一致时有几种解决方案使用SQL别名var sql SELECT ProductID AS Id, ProductName AS Name FROM Products;自定义映射public class ProductMap : SqlMapper.ITypeMap { // 实现接口方法... } // 注册自定义映射 SqlMapper.SetTypeMap(typeof(Product), new ProductMap());使用特性标注public class Product { [Column(ProductID)] public int Id { get; set; } [Column(ProductName)] public string Name { get; set; } }6.2 处理NULL值Dapper默认会将数据库NULL映射为C#的null对于值类型public class Product { public int? Rating { get; set; } // 可空类型处理数据库NULL }6.3 大数据集处理对于大型数据集使用缓冲查询// 默认缓冲适合小数据集 var products await connection.QueryAsyncProduct(SELECT * FROM Products); // 非缓冲查询适合大数据集 var unbufferedProducts await connection.QueryUnbufferedAsyncProduct( SELECT * FROM Products);6.4 动态结果处理当返回的列不固定时使用dynamic类型var results await connection.QueryAsync( SELECT Id, Name FROM Products WHERE Price Price, new { Price 50 }); foreach (dynamic product in results) { Console.WriteLine(${product.Id}: {product.Name}); }7. 实际应用案例7.1 分页查询实现实现高效的分页查询public async Task(IEnumerableProduct Data, int TotalCount) GetPagedProductsAsync( int pageNumber, int pageSize, string searchTerm null) { await using var connection _dbContext.CreateConnection(); var whereClause string.IsNullOrEmpty(searchTerm) ? string.Empty : WHERE Name LIKE SearchTerm; var sql $ SELECT * FROM Products {whereClause} ORDER BY Id OFFSET Offset ROWS FETCH NEXT PageSize ROWS ONLY; SELECT COUNT(*) FROM Products {whereClause}; using var multi await connection.QueryMultipleAsync(sql, new { SearchTerm $%{searchTerm}%, Offset (pageNumber - 1) * pageSize, PageSize pageSize }); var products await multi.ReadAsyncProduct(); var totalCount await multi.ReadSingleAsyncint(); return (products, totalCount); }7.2 复杂报表查询处理复杂的报表数据public async TaskSalesReport GetSalesReportAsync(DateTime startDate, DateTime endDate) { await using var connection _dbContext.CreateConnection(); var sql SELECT p.Id, p.Name, SUM(oi.Quantity) AS TotalSold, SUM(oi.Quantity * oi.UnitPrice) AS TotalRevenue FROM Products p JOIN OrderItems oi ON p.Id oi.ProductId JOIN Orders o ON oi.OrderId o.Id WHERE o.OrderDate BETWEEN StartDate AND EndDate GROUP BY p.Id, p.Name ORDER BY TotalRevenue DESC; var reportItems await connection.QueryAsyncSalesReportItem(sql, new { StartDate startDate, EndDate endDate }); var summarySql SELECT COUNT(DISTINCT o.Id) AS TotalOrders, SUM(oi.Quantity * oi.UnitPrice) AS GrandTotal FROM Orders o JOIN OrderItems oi ON o.Id oi.OrderId WHERE o.OrderDate BETWEEN StartDate AND EndDate; var summary await connection.QuerySingleAsyncSalesSummary(summarySql, new { StartDate startDate, EndDate endDate }); return new SalesReport { Items reportItems, Summary summary, StartDate startDate, EndDate endDate }; }7.3 使用Dapper Contrib简化CRUD对于简单的CRUD操作可以使用Dapper.Contrib// 安装Dapper.Contrib dotnet add package Dapper.Contrib // 使用特性标注实体 [Table(Products)] public class Product { [Key] public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } // 简化CRUD操作 public async TaskProduct GetProductAsync(int id) { await using var connection _dbContext.CreateConnection(); return await connection.GetAsyncProduct(id); } public async Taskint InsertProductAsync(Product product) { await using var connection _dbContext.CreateConnection(); return await connection.InsertAsync(product); } public async Taskbool UpdateProductAsync(Product product) { await using var connection _dbContext.CreateConnection(); return await connection.UpdateAsync(product); } public async Taskbool DeleteProductAsync(int id) { await using var connection _dbContext.CreateConnection(); return await connection.DeleteAsync(new Product { Id id }); }
RELATED

相关推荐

智能系统架构的演进与实践:从静态设计到动态进化

智能系统架构的演进与实践:从静态设计到动态进化

1. 系统架构的智能化演进背景十年前我们设计系统时,画完UML图、定好接口规范就能交付一套"完成态"的架构。如今在AI驱动的业务场景中,这种静态设计模式正在被彻底颠覆。上周我负责的电商推荐系统刚完成第37次架构自动调整——基于实时流量特征…

📅 2026/11/2 23:00:25
DeepSeek Harness桌面端架构解析:Electron+Node+Python技术栈深度实践

DeepSeek Harness桌面端架构解析:Electron+Node+Python技术栈深度实践

1. 项目概述:这不是“又一个桌面端”,而是本地AI工作流的临界点突破最近在技术圈刷到一条消息:“DeepSeek Harness 出了桌面端?”——标题里那个“扒了一遍”的动词,瞬间勾住了我。不是因为好奇它长什么样,…

📅 2026/11/2 22:55:25
用C++构建高性能AI Agent:架构设计与实战避坑指南

用C++构建高性能AI Agent:架构设计与实战避坑指南

你有没有想过这个问题:Python 几乎主导了 AI Agent 的开发,为什么还有人非要拿 C 重写一套?说实话,两年前我自己也不理解。直到真碰上一个需求——在一个超大流量的网关服务里嵌入一个能自动编排任务的 Agent,Python 的…

📅 2026/11/2 22:55:25
MORE NEWS

更多资讯

📰

awesome-agent-skills 完全对比:Agent Skills vs MCP vs 微调,智能体能力增强到底怎么选?

【免费下载链接】awesome-agent-skills The definitive resource for Agent Skills - modular capabilities revolutionizing AI agent architecture 项目地址: https://gitcode.com/gh_mirrors/awes/awesome-agent-skills 点击查看 免费下载 Agent Skills&#xf…

📰

九联UNT402A刷Armbian一次学会: S905L3机顶盒变身7×24小时Linux家用服务器

九联UNT402A刷Armbian一次学会: S905L3机顶盒变身724小时Linux家用服务器 【免费下载链接】amlogic-s9xxx-armbian Supports running Armbian on Amlogic, Allwinner, and Rockchip devices. Support a311d, s922x, s905x3, s905x2, s912, s905d, s905x, s905w, s905, s905l, r…

📰

123.Python——OpenCV实现视频窗口画中画的效果

这里把实现方法的具体说明拿出来给大家看看。1、先把主画面那边的摄像头视频给打开。然后再把显示这个视频的主窗口拿出来呈现给你看。2、与此同时, 把那个子画面里面的视频文件给打开了, 当然呢, 你也可以去打开任意一个视频流。3、先对子画面的大小进行缩放处理, 然后利用图像…

📰

金融科技FDE:连接产品与客户的桥梁,小白程序员必备的进阶之路

本文深入探讨了金融科技领域FDE(前线部署工程师)的核心价值与必备技能。FDE是弥合产品能力与客户真实需求、监管要求的桥梁,需具备软件工程、咨询及金融领域知识。文章解析了金融科技FDE的独特性,如合规约束、遗留系统集成、监管截…

📰

Java FRQ 001 - If condition

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

PLFM雷达原理与10.5GHz相控阵开源实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬