C#.NET dapper 详解

简介

Dapper 是由 Stack Overflow 团队开发的一个简单、高性能的微型 ORM(Object‑Relational Mapper),仅几千行代码,依赖于 ADO.NET 的 IDbConnection,通过动态生成 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 Core,Dapper 的查询速度常常有 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.tpcf.cn/diannao/93107.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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…