C#.NET dapper 详解

简介

Dapper 是由 Stack Overflow 团队开发的一个简单、高性能的微型 ORM(Object‑Relational Mapper),仅几千行代码,依赖于 ADO.NETIDbConnection,通过动态生成 IL 来映射结果到实体对象。

EF、NHibernate 这类全功能 ORM 相比,Dapper 提供了更直接、更接近 SQL 的操作方式,性能非常接近手写 ADO.NET

基本用法

安装与配置
dotnet add package Dapper
dotnet add package Dapper.Contrib
dotnet add package System.Data.SqlClient # SQL Server
打开连接
using (var conn = new SqlConnection(connectionString))
{conn.Open();// … 执行 Dapper 操作
}
查询单个实体
string sql = "SELECT Id, Name, Age FROM Users WHERE Id = @Id";
var user = conn.QueryFirstOrDefault<User>(sql, new { Id = 123 });
  • QueryFirstOrDefault<T>:返回第一行并映射为 T,无数据时返回 default(T)

  • 还有 QuerySingle<T>、Query<T>(返回 IEnumerable<T>)等方法。

查询多个对象
using (var connection = new SqlConnection(connectionString))
{connection.Open();var users = connection.Query<User>("SELECT * FROM Users WHERE Age > @Age", new { Age = 18 });return users.ToList();
}
执行增删改
string insert = "INSERT INTO Users (Name, Age) VALUES (@Name, @Age)";
int rows = conn.Execute(insert, new { Name = "Alice", Age = 28 });
  • Execute:用于执行不返回结果集的 SQL,返回受影响行数。
基础 CRUD 操作
using var connection = new SqlConnection(connectionString);// 查询
var product = connection.QueryFirstOrDefault<Product>("SELECT * FROM Products WHERE Id = @Id", new { Id = 1 });// 插入
var newProduct = new Product { Name = "Laptop", Price = 999.99m };
var insertedId = connection.ExecuteScalar<int>("INSERT INTO Products (Name, Price) OUTPUT INSERTED.Id VALUES (@Name, @Price)",newProduct);// 更新
var rowsAffected = connection.Execute("UPDATE Products SET Price = @Price WHERE Id = @Id",new { Id = 1, Price = 899.99m });// 删除
connection.Execute("DELETE FROM Products WHERE Id = @Id", new { Id = 10 });

关键方法详解

方法描述示例
Query<T>返回实体列表connection.Query<Product>("SELECT * FROM Products")
QueryFirst<T>返回第一条结果(无结果抛异常)connection.QueryFirst<Product>("SELECT ... WHERE Id=@id", new {id=1})
QueryFirstOrDefault<T>返回第一条或默认值(无结果返回 null)connection.QueryFirstOrDefault<Product>(...)
QuerySingle<T>返回单条结果(结果不唯一抛异常)适用于主键查询
Execute执行非查询操作(增删改),返回受影响行数connection.Execute("DELETE FROM Products WHERE Id=@id", new {id=10})
ExecuteScalar<T>返回单个值(如 COUNT、SUM)int count = connection.ExecuteScalar<int>("SELECT COUNT(*) FROM Products")

高级查询技术

多结果集处理
using var multi = connection.QueryMultiple("SELECT * FROM Products; SELECT * FROM Categories");var products = multi.Read<Product>();
var categories = multi.Read<Category>();
多表关联映射(Multi‑Mapping)

当一个查询返回多张表的数据,需要映射到不同实体并组合起来时:

string sql = @"
SELECT o.Id, o.OrderDate,c.Id, c.Name 
FROM Orders o
JOIN Customers c ON o.CustomerId = c.Id";var list = conn.Query<Order, Customer, Order>(sql,(order, cust) => { order.Customer = cust; return order; },splitOn: "Id");
  • splitOn 指定从哪个列开始分割下一张表的映射。
一对多关系映射
var sql = @"SELECT o.*, i.* FROM Orders oINNER JOIN OrderItems i ON o.Id = i.OrderIdWHERE o.CustomerId = @CustomerId";var orderDictionary = new Dictionary<int, Order>();var orders = connection.Query<Order, OrderItem, Order>(sql,(order, item) =>{if (!orderDictionary.TryGetValue(order.Id, out var existingOrder)){existingOrder = order;existingOrder.Items = new List<OrderItem>();orderDictionary.Add(existingOrder.Id, existingOrder);}existingOrder.Items.Add(item);return existingOrder;},new { CustomerId = 123 },splitOn: "Id");

Dapper.Contrib 扩展

自动 CRUD 操作
// 实体类标记
[Table("Products")]
public class Product
{[Key]public int Id { get; set; }public string Name { get; set; }public decimal Price { get; set; }
}// 自动操作
var product = connection.Get<Product>(1);          // 查询
connection.Insert(new Product { ... });           // 插入
connection.Update(product);                       // 更新
connection.Delete(product);                       // 删除// 批量操作
connection.InsertAll(products);                   // 批量插入
自定义映射规则
SqlMapper.SetTypeMap(typeof(Product),new CustomPropertyTypeMap(typeof(Product),(type, columnName) => type.GetProperties().FirstOrDefault(prop => prop.GetCustomAttributes(false).OfType<ColumnAttribute>().Any(attr => attr.Name == columnName))));

性能优化技巧

参数化查询最佳实践
// 正确:参数化防止SQL注入
connection.Query("SELECT * FROM Users WHERE Name = @Name", new { Name = "Alice" });// 错误:拼接SQL风险
connection.Query($"SELECT * FROM Users WHERE Name = '{"Alice"}'");
批处理与事务
using var transaction = connection.BeginTransaction();try
{// 批量插入connection.Execute("INSERT INTO Logs (Message) VALUES (@Message)",logs.Select(l => new { l.Message }),transaction);// 批量更新connection.Execute("UPDATE Products SET Stock = Stock - @Quantity WHERE Id = @Id",orderItems.Select(i => new { i.Quantity, i.ProductId }),transaction);transaction.Commit();
}
catch
{transaction.Rollback();throw;
}
异步操作支持
public async Task<Product> GetProductAsync(int id)
{using var conn = new SqlConnection(connectionString);return await conn.QueryFirstOrDefaultAsync<Product>("SELECT * FROM Products WHERE Id = @Id", new { Id = id });
}

高级应用场景

动态查询构建
public IEnumerable<Product> SearchProducts(ProductSearchCriteria criteria)
{var sql = new StringBuilder("SELECT * FROM Products WHERE 1=1");var parameters = new DynamicParameters();if (!string.IsNullOrEmpty(criteria.Name)){sql.Append(" AND Name LIKE @Name");parameters.Add("Name", $"%{criteria.Name}%");}if (criteria.MinPrice.HasValue){sql.Append(" AND Price >= @MinPrice");parameters.Add("MinPrice", criteria.MinPrice);}return connection.Query<Product>(sql.ToString(), parameters);
}
JSON 数据处理(SQL Server 2016+)
// 查询JSON列
var orders = connection.Query<Order>(@"SELECT Id,JSON_VALUE(Details, '$.Customer.Name') AS CustomerName,JSON_VALUE(Details, '$.TotalAmount') AS TotalAmountFROM Orders");// 插入JSON数据
connection.Execute("INSERT INTO Orders (Details) VALUES (@Details)",new { Details = JsonConvert.SerializeObject(orderDetails) });

参数化查询

Dapper 默认将匿名对象的属性映射到 SQL 中的参数,杜绝 SQL 注入:

var parameters = new DynamicParameters();
parameters.Add("MinPrice", 100);
parameters.Add("MaxPrice", 500);
var products = conn.Query<Product>("SELECT * FROM Products WHERE Price BETWEEN @MinPrice AND @MaxPrice",parameters);
  • DynamicParameters 支持输出参数、存储过程参数等高级场景。

最佳实践指南

与 EF Core 混合使用
// 复杂查询用 Dapper
public List<ReportItem> GetSalesReport(DateTime start, DateTime end)
{return _dapperConnection.Query<ReportItem>(...);
}// 事务性操作用 EF Core
public void PlaceOrder(Order order)
{using var transaction = _dbContext.Database.BeginTransaction();try{_dbContext.Orders.Add(order);_dbContext.SaveChanges();// 调用库存服务(Dapper)_dapperConnection.Execute(...);transaction.Commit();}catch{transaction.Rollback();throw;}
}
性能关键路径优化
// 缓存查询结果(高频读取)
private static readonly string ProductByIdSql = "SELECT * FROM Products WHERE Id = @Id";public Product GetProduct(int id)
{return _connection.QueryFirstOrDefault<Product>(ProductByIdSql, new { Id = id });
}// 使用存储过程
var products = _connection.Query<Product>("usp_GetProductsByCategory", new { CategoryId = 5 }, commandType: CommandType.StoredProcedure);

Dapper 扩展生态

常用扩展库
库名功能安装命令
Dapper.Contrib基础CRUD扩展Install-Package Dapper.Contrib
Dapper.FluentMap高级映射配置Install-Package Dapper.FluentMap
Dapper.SimpleCRUD自动化CRUD操作Install-Package Dapper.SimpleCRUD
Dapper.SqlBuilder动态SQL构建Install-Package Dapper.SqlBuilder
数据库提供程序支持
// PostgreSQL
using var conn = new NpgsqlConnection(pgConnectionString);// MySQL
using var conn = new MySqlConnection(mysqlConnectionString);// SQLite
using var conn = new SQLiteConnection(sqlliteConnectionString);

Dapper 适用场景分析

推荐使用场景

  • 高性能报表查询:大数据量复杂查询

  • 批量数据处理:ETL 管道操作

  • 微服务架构:轻量级数据访问层

  • 遗留系统集成:替代原生 ADO.NET

  • 读写分离架构:读操作专用

不推荐场景

  • 复杂领域模型:需要变更跟踪

  • 数据库迁移需求:缺乏迁移工具

  • 自动 SQL 生成:需手写 SQL

  • 简单 CRUD 应用:EF Core 更高效

总结:Dapper 核心价值

  • 极致性能:接近原生 ADO.NET 的执行效率

  • 精细控制:完全掌控 SQL 语句

  • 轻量灵活:最小化 ORM 开销

  • 易于集成:与现有项目无缝结合

  • 丰富扩展:强大的社区生态支持

存储过程调用

var users = conn.Query<User>("sp_GetActiveUsers",new { IsActive = true },commandType: CommandType.StoredProcedure);
  • 也可用 Execute、QueryMultiple(一次取多组结果)等。

性能优势

  • 由于 Dapper 直接在运行时生成 IL 来做对象映射,并且复用 ADO.NET 连接与命令,对大量并发、批量查询场景性能非常优秀。

  • 对比 EF CoreDapper 的查询速度常常有 2–10 倍优势,尤其是返回扁平对象时。

与 EF 的对比

DapperEF Core
编写 SQL手写、完全自定义LINQ 语句
配置映射自动映射或手动配置(少量)属性与 Fluent API
性能极高,接近原生 ADO.NET较高,但 LINQ 翻译与变更追踪有额外开销
灵活度完全掌控 SQL高,受限于 LINQ 表达式能力
学习成本相对高(迁移、关系、导航属性等)

常见问题与最佳实践

  • 连接管理:建议每次操作单独 using 打开连接,避免长连接导致资源占用。

  • 参数重用:对于频繁相同参数的查询,可重用 DynamicParameters 对象,提升性能。

  • 大型结果集:如果结果集很大,使用 Query<T> 时可结合 AsList() 分批处理,或使用 IDataReader 手动读取。

  • 分页:可在 SQL 中直接使用 OFFSET…FETCH,或结合动态参数快速实现。

  • 缓存:Dapper 自带简单的 SQL 缓存(IL 生成后会缓存映射),但不缓存查询结果;如需缓存结果,可加分布式/内存缓存。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/93107.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/93107.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode 热题 100】35. 搜索插入位置——二分查找(左闭右开)

Problem: 35. 搜索插入位置 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 文章目录整体思路完整代码时空复杂度时间…

Python-初学openCV——图像预处理(四)——滤波器

目录 一、图像噪点消除噪声&#xff1a; 1、概念 2、均值滤波 3、方框滤波 4 、高斯滤波 5、中值滤波 6、双边滤波 7、总结 一、图像噪点消除噪声&#xff1a; 1、概念 指图像中的一些干扰因素&#xff0c;通常是由图像采集设备、传输信道等因素造成的&#xff0c;表现…

嵌入式系统可靠性设计

嵌入式系统可靠性设计硬件件可靠性设计1. 硬件设计原则2. 硬件设计注意问题2.1 引脚布局和走线2.2 元器件选择和布局2.3 电源和地线分离2.4 EMI/EMC设计2.5 系统可靠性2.6 资源利用和扩展性软件可靠性设计1. 设计原则1.1 模块化设计1.2 冗余设计1.3 容错设计1.4 实时性保障1.5 …

cJSON在STM32单片机上使用遇到解析数据失败问题

我们在单片机上解析JSON格式时&#xff08;比如在用云平台物联网开发时&#xff09;&#xff0c;可以直接使用cJson库来完成自己的操作&#xff0c;而不需要单独实现&#xff0c;具体使用方法可以搜一下。 cJson&#xff1a;一个基于 C 语言的 Json 库&#xff0c;它是一个开源…

python3基础语法梳理(三)

接上一篇博客 &#x1f3ae; 猜数字小游戏 - Python版 &#x1f9e0; 游戏规则&#xff1a; 系统随机生成一个 1 到 10 的整数玩家输入猜测的数字使用 if 语句判断玩家猜得是否正确提示“猜对了”或“太大/太小了” import randomsecret_number random.randint(1, 10) att…

【docker】将已有mysql脚本导入镜像内使用

准备SQL脚本将SQL脚本&#xff08;如init.sql&#xff09;放在宿主机目录下&#xff0c;例如&#xff1a;/path/to/sql-scripts/init.sql启动MySQL容器并挂载脚本使用 -v 参数将SQL脚本挂载到容器的初始化目录&#xff1a;docker run --name mysql-container \-e MYSQL_ROOT_PA…

【机器学习深度学习】LLamaFactory微调效果与vllm部署效果不一致如何解决

目录 前言 一、问题本质 1.1 问题说明 1.2 问题本质示意 二、常见原因 LLaMAFactory对话模板规则定义 模型对话模板定义规则 三、解决方法 提取代码myset.py 创建jinja文件 安装VLLM 运行VLLM 安装运行open webui流程 四、流程梳理 前言 本文主要讲述的主要内容…

Python入门构建网页

用纯 Python 构建 Web 应用 本教程将带你从零开始&#xff0c;构建一个交互式的待办事项清单。 fasthtml 的核心哲学是“回归初心&#xff0c;大道至简”。在当今复杂的前后端分离技术栈中 &#xff0c;它提供了一条返璞归真的路径&#xff0c;旨在让你能用纯粹的 Python 构建从…

开源 Arkts 鸿蒙应用 开发(九)通讯--tcp客户端

文章的目的为了记录使用Arkts 进行Harmony app 开发学习的经历。本职为嵌入式软件开发&#xff0c;公司安排开发app&#xff0c;临时学习&#xff0c;完成app的开发。开发流程和要点有些记忆模糊&#xff0c;赶紧记录&#xff0c;防止忘记。 相关链接&#xff1a; 开源 Arkts …

Go的defer和recover

在 Go 语言中&#xff0c;defer 和 recover 是两个紧密相关的关键字&#xff0c;主要用于错误处理和资源清理。它们通常一起使用&#xff0c;特别是在处理panic&#xff08;运行时崩溃&#xff09;时&#xff0c;确保程序不会直接崩溃&#xff0c;而是能够优雅地恢复并继续执行…

Spring Boot 配置文件常用配置属性详解(application.properties / application.yml)

前言 Spring Boot 的一大优势就是通过简单的配置文件即可快速定制应用行为&#xff0c;而无需编写大量 XML 配置或 Java 代码。Spring Boot 使用 application.properties 或 application.yml 作为核心配置文件&#xff0c;支持丰富的配置属性。 本文将详细介绍 Spring Boot 常用…

uni-appDay02

1.首页-通用轮播组件 轮播图组件需要再首页和分类页使用&#xff0c;封装成通用组件 准备组件自动导入组件 <script setup lang"ts"> import XtxSwiper from /components/XtxSwiper.vue import CustomNavbar from ./components/CustomNavbar.vue </scrip…

FastAPI入门:请求体、查询参数和字符串校验、路径参数和数值校验

请求体 FastAPI 使用请求体从客户端&#xff08;例如浏览器&#xff09;向 API 发送数据。请求体是客户端发送给 API 的数据。响应体是 API 发送给客户端的数据。 使用 Pydantic 模型声明请求体&#xff0c;能充分利用它的功能和优点 from fastapi import FastAPI from pydanti…

Docker的docker-compose类比Spring的ApplicationContext

总一句话是&#xff1a;Docker Compose&#xff1a;集中化管理多个容器及其依赖的资源环境&#xff1b;ApplicationContext&#xff1a;集中化管理 多个Bean 及其运行所需的资源和依赖关系。 1. 整体概念 Docker Compose&#xff1a;用于定义和运行多容器 Docker 应用程序&…

Reason-before-Retrieve(CVPR 2025)

研究方向&#xff1a;Image Captioning论文全名&#xff1a;《Reason-before-Retrieve: One-Stage Reflective Chain-of-Thoughts for Training-Free Zero-Shot Composed Image Retrieval》1. 论文介绍组合图像检索&#xff08;CIR&#xff09;旨在检索与参考图像密切相似的目标…

Idefics2:构建视觉-语言模型时,什么是重要的

温馨提示&#xff1a; 本篇文章已同步至"AI专题精讲" Idefics2&#xff1a;构建视觉-语言模型时&#xff0c;什么是重要的 摘要 随着large language models和vision transformers的进步&#xff0c;视觉-语言模型&#xff08;VLMs&#xff09;受到了越来越多的关注…

再谈fpga开发(fpga调试方法)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】我们之前在学校学习c、c的时候&#xff0c;其实学校漏掉了很重要的一个教学环节&#xff0c;那就是调试、测试。很多时候我们代码写出来了&#xff…

C语言中的数据结构--栈和队列(1)

前言本届开始我们将对数据结构中栈的内容进行讲解,那么废话不多说,我们正式进入今天的学习栈栈是一种很特殊的线性表&#xff0c;它只能在固定的一端进行插入和删除操作&#xff0c;进行数据的插入和删除的一端叫做栈顶&#xff0c;另外一端叫做栈底&#xff0c;栈中的元素遵守…

字符串是数据结构还是数据类型?

比较纠结的一个问题&#xff0c;以下是在网上查到后总结的&#xff0c;不知道对不对&#xff0c;欢迎讨论。这是个触及计算机科学核心概念的精妙问题&#xff01;字符串既可以被视为一种数据类型&#xff0c;也可以被视为一种数据结构&#xff0c;这取决于你观察的视角和讨论的…

Cline与Cursor深度实战指南:AI编程助手的革命性应用

引言 在AI编程工具快速发展的今天&#xff0c;Cline和Cursor作为两款备受瞩目的AI编程助手&#xff0c;正在重新定义开发者的工作方式。作为一名深度使用这两款工具的开发者&#xff0c;我在过去一年的实践中积累了丰富的经验和独到的见解。本文将从技术角度深入分析Cline和Cur…