C#进阶:轻量级ORM框架Dapper详解
C#进阶:轻量级ORM框架Dapper详解
在C#开发中,ORM(对象关系映射)框架是处理数据库交互的重要工具。Dapper作为一个轻量级的ORM框架,专为.NET平台设计,因其高性能和易用性而备受开发者青睐。本文将详细介绍Dapper的基本概念、与其他持久层框架的比较、基本及高级语法的使用,并通过实例展示如何在项目中集成和使用Dapper。
一、Dapper的基本概念
Dapper是一个轻量级的ORM框架,由Stack Overflow团队开发并开源。它通过扩展IDbConnection接口,简化了数据库访问过程,允许开发者直接执行SQL查询,并将查询结果映射到C#对象模型中。Dapper的核心优势在于其轻量、高效和易用性,非常适合需要高效数据访问的场景。
二、Dapper与其他持久层框架的比较
为了更清晰地展示Dapper的优势,我们可以通过比较它与其他数据库操作框架来进行分析。以下是一些常见的数据库操作框架与Dapper的比较:
- Entity Framework (EF):EF是微软官方推出的一个完整的ORM框架,提供了丰富的功能,如变更跟踪、延迟加载等。然而,EF的复杂性也带来了额外的性能开销。相比之下,Dapper更加轻量,专注于提供高效的数据库访问能力,没有复杂的上下文管理和变化跟踪机制。
- ADO.NET:ADO.NET是.NET平台下用于数据库访问的一组类库,它提供了数据库连接、命令执行等底层功能。Dapper可以看作是ADO.NET的扩展,通过提供高级的抽象和简化的API,使开发者能够更高效地执行数据库操作。
- Dapper.Contrib:Dapper.Contrib是Dapper的一个扩展库,提供了更丰富的功能,如自动映射、CRUD操作等。然而,Dapper本身已经足够轻量且强大,对于大多数场景来说,直接使用Dapper即可满足需求。
三、Dapper的基本使用
1. 安装Dapper
首先,你需要在项目中安装Dapper。通过NuGet包管理器可以轻松添加Dapper到你的项目中:
Install-Package Dapper
2. 建立数据库连接
使用ADO.NET的IDbConnection接口建立数据库连接。Dapper不限制你使用哪种数据库连接,只要它实现了IDbConnection接口即可。
string connectionString = "YourConnectionStringHere";
using (IDbConnection db = new SqlConnection(connectionString))
{// 数据库操作
}
3. 执行SQL查询
Dapper提供了多种扩展方法来执行SQL查询。例如,使用Query
方法执行查询并返回结果集:
string sql = "SELECT * FROM Users";
var users = db.Query<User>(sql).ToList();
对于单条记录的查询,可以使用QueryFirstOrDefault
方法:
var user = db.QueryFirstOrDefault<User>("SELECT * FROM Users WHERE Id = @Id", new { Id = 1 });
4. 执行插入、更新和删除操作
Dapper使用Execute
方法执行插入、更新和删除操作。这些方法返回受影响的行数。
- 插入操作:
string insertSql = "INSERT INTO Users (Name, Email) VALUES (@Name, @Email)";
var newUserId = db.ExecuteScalar<int>(insertSql + "; SELECT CAST(SCOPE_IDENTITY AS INT)", new { Name = "NewUser", Email = "newuser@example.com" });
- 更新操作:
string updateSql = "UPDATE Users SET Name = @Name WHERE Id = @Id";
var updatedRows = db.Execute(updateSql, new { Name = "UpdatedUser", Id = 1 });
- 删除操作:
string deleteSql = "DELETE FROM Users WHERE Id = @Id";
var deletedRows = db.Execute(deleteSql, new { Id = 1 });
5. 参数化查询
Dapper支持参数化查询,这有助于防止SQL注入攻击。在SQL语句中使用参数占位符,并将参数作为匿名对象传递给Execute
或Query
方法:
string query = "SELECT * FROM Students WHERE LastName = @LastName";
var students = db.Query<Student>(query, new { LastName = "Doe" }).ToList();
6. 缓存查询计划
Dapper会缓存查询计划,减少SQL解析和执行的开销。这意味着对于相同的查询(包括参数化查询),Dapper可以更快地执行它们。
四、Dapper的高级特性
1. 多表查询和结果映射
Dapper支持多表查询,并将结果映射到多个对象。这通常通过Query
方法的重载版本或自定义类型处理器(Type Handler)来实现。
多表查询和结果映射示例:
假设我们有两个表,一个是Users
(用户表),另一个是UserDetails
(用户详情表),现在我们想要通过一个查询同时获取用户及其详细信息。
首先,定义两个C#类来表示这两个表:
public class User
{public int Id { get; set; }public string Name { get; set; }public UserDetail Detail { get; set; }
}public class UserDetail
{public int UserId { get; set; }public string Address { get; set; }// 其他用户详情字段...
}
但是,直接映射到一个嵌套对象可能不太直接,因为Dapper默认不支持直接映射到嵌套对象。一种常见的解决方案是使用匿名类型或者DTO(数据传输对象)来接收查询结果,然后在应用层进行手动组装。不过,对于简单的嵌套关系,我们可以考虑使用splitOn
参数来告诉Dapper如何分割结果到不同的对象。但请注意,这种方法更适合简单的嵌套关系,并且splitOn
依赖于结果集中某个字段的值来分割数据。
然而,对于更复杂的场景,我们通常会使用一个DTO或视图模型(ViewModel)来接收查询结果,并在查询后手动组装对象。
使用DTO和手动组装的示例:
public class UserDto
{public int Id { get; set; }public string Name { get; set; }public string Address { get; set; }// 其他需要的字段...
}// 假设我们有一个联合查询,同时获取用户和详情
string sql = @"
SELECT u.Id, u.Name, ud.Address
FROM Users u
INNER JOIN UserDetails ud ON u.Id = ud.UserId
WHERE u.Name = @Name";var userDtos = db.Query<UserDto, UserDto, UserDto>(sql, (user, detail) => {// 注意:这里我们实际上没有用到第二个和第三个UserDto参数,因为这是一个简化的例子// 在真实场景中,你可能需要处理更复杂的映射关系// 但由于Dapper不支持直接映射到嵌套对象,我们采用DTO来接收所有数据// 并在查询后手动处理(尽管在这个例子中没有展示)// 或者,你可能需要执行两次查询,分别获取用户和详情,然后在内存中进行组装return user; // 这里返回user作为示例,实际上你可能需要更复杂的逻辑}, new { Name = "JohnDoe" }, splitOn: "Id").ToList(); // 注意:splitOn在这里不适用,因为这里只是一个示例// 实际上,上面的代码逻辑是不正确的,因为它试图用splitOn来处理一个本不需要分割的场景
// 正确的做法可能是执行两个查询或使用DTO和手动组装// 正确的DTO使用方式示例(假设我们已经有了两个分开的查询结果)
// ...(此处省略具体的查询和组装逻辑)
请注意,上面的Query
方法使用示例并不完全正确,因为它试图用splitOn
来处理一个不需要分割的查询结果。在实际应用中,对于复杂的多表查询和结果映射,你可能需要:
- 执行多个单独的查询,并在内存中将结果组装成你需要的对象结构。
- 使用DTO(数据传输对象)来接收查询结果,并在查询之后(可能是在服务层或应用层)进行手动组装。
- 考虑使用更高级的ORM功能,如Entity Framework的导航属性,如果你的项目可以接受EF带来的额外复杂性和性能开销。
2. 异步查询
Dapper支持异步数据库操作,这有助于提高应用程序的响应性和可伸缩性。你可以使用QueryAsync
、ExecuteAsync
等异步方法来执行数据库操作。
string sql = "SELECT * FROM Users";
var users = await db.QueryAsync<User>(sql).ToListAsync();
五、Dapper的最佳实践
- 使用参数化查询:防止SQL注入攻击。
- 合理设计数据库查询:尽量优化SQL查询,减少不必要的数据传输和处理。
- 考虑使用DTO:对于复杂的数据传输场景,使用DTO可以减少数据传输量,并有助于解耦数据库模型和应用程序模型。
- 利用Dapper的缓存功能:Dapper会缓存查询计划,但对于频繁变化的数据,考虑使用更细粒度的缓存策略。
- 异步编程:在可能的情况下,使用Dapper的异步API来提高应用程序的响应性和可伸缩性。
- 错误处理:确保妥善处理数据库操作中的错误和异常,如连接失败、查询超时等。
六、Dapper的高级用法
1. 动态SQL构建
在复杂的应用场景中,有时候SQL查询的具体结构(如表名、字段名或条件)在运行时才能确定。Dapper虽然不直接提供SQL构建器,但你可以结合C#的字符串拼接、表达式树或第三方库(如Dapper-Contrib、DapperExtensions或SqlKata)来动态构建SQL语句。
使用字符串拼接时,需要特别注意SQL注入的风险,并确保所有动态内容都通过参数化查询来传递。
2. 批处理操作
对于需要执行大量相似操作(如插入多行数据)的场景,批处理可以显著提高性能。Dapper没有内置的批处理API,但你可以通过Execute
方法多次执行相同的SQL命令(尽管这不是真正的批处理),或者利用数据库特定的批处理语法(如SQL Server的表值参数或MySQL的批量插入语法)来手动实现。
另外,你也可以考虑使用ADO.NET的SqlBulkCopy
(对于SQL Server)或类似的数据库特定功能来实现高效的批量数据操作。
3. 事务管理
Dapper本身不直接提供事务管理功能,但它可以与.NET的TransactionScope
或数据库连接的事务对象结合使用来管理事务。
使用TransactionScope
时,你可以轻松地将多个数据库操作包含在单个事务中,并确保它们要么全部成功,要么在遇到错误时全部回滚。
using (var scope = new TransactionScope())
{// 执行数据库操作...// 如果所有操作都成功,则自动提交事务scope.Complete();
}
如果你使用的是数据库连接的事务对象(如SqlTransaction
),则需要手动控制事务的开始、提交和回滚。
4. 插件和扩展
Dapper社区提供了许多插件和扩展,以增强Dapper的功能。这些插件可能包括:
- Dapper-Contrib:提供了基于约定的CRUD操作,减少了样板代码。
- DapperExtensions:提供了更高级的查询构建和映射功能。
- SqlKata:一个强大的SQL查询构建器,可以与Dapper结合使用来构建复杂的SQL查询。
使用这些插件和扩展时,请确保它们与你的项目需求和数据库环境兼容,并仔细阅读文档以了解如何正确集成和使用它们。
七、Dapper的局限性
尽管Dapper是一个轻量级且功能强大的ORM工具,但它也有一些局限性:
-
缺乏内置的高级功能:与EF Core等更全面的ORM相比,Dapper提供了更少的内置功能,如自动变更跟踪、复杂关系映射和延迟加载等。
-
SQL依赖:由于Dapper允许直接编写SQL语句,因此你的代码将紧密依赖于特定的数据库实现。这可能会增加在不同数据库之间迁移的难度。
-
错误处理和日志记录:Dapper本身不提供内置的错误处理和日志记录功能。你需要自己实现这些功能,或使用其他库(如log4net、Serilog等)来记录数据库操作的日志。
-
学习曲线:虽然Dapper的API相对简单,但编写高效的SQL查询和了解数据库的最佳实践仍然需要一定的学习和经验。
八、总结
Dapper是一个轻量级、高性能的.NET ORM工具,它允许开发者直接编写SQL语句并映射到.NET对象。通过提供简单的API和高效的性能,Dapper成为许多.NET开发者的首选ORM之一。然而,它也有一些局限性,如缺乏内置的高级功能和SQL依赖等。在使用Dapper时,建议结合项目需求、数据库环境和开发团队的经验来评估其适用性,并考虑使用插件和扩展来增强其功能。同时,也需要注意SQL注入的风险,并确保编写高效的SQL查询和合理的数据库操作。
相关文章:
C#进阶:轻量级ORM框架Dapper详解
C#进阶:轻量级ORM框架Dapper详解 在C#开发中,ORM(对象关系映射)框架是处理数据库交互的重要工具。Dapper作为一个轻量级的ORM框架,专为.NET平台设计,因其高性能和易用性而备受开发者青睐。本文将详细介绍D…...

【python015】常见成熟AI-图像识别场景算法清单(已更新)
1.欢迎点赞、关注、批评、指正,互三走起来,小手动起来! 【python015】常见成熟AI-图像识别场景算法清单及代码【python015】常见成熟AI-图像识别场景算法清单及代码【python015】常见成熟AI-图像识别场景算法清单及代码 文章目录 1.背景介绍2…...
删除有序数组中的重复项(LeetCode)
题目 给你一个 升序排列 的数组 ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 中唯一元素的个数。 考虑 的唯一元素的数量为 ,你需要做以下事情确…...

【算法 03】雇佣问题
“雇用问题”及其算法优化 在日常生活和工作中,我们经常会遇到需要从多个选项中做出选择的情况,而“雇用问题”正是这样一个典型的例子。在这个问题中,我们不仅要考虑如何高效地找到最佳候选人,还要关注整个过程中的成本。今天&a…...

vue3+axios请求导出excel文件
在Vue 3中使用axios请求导出Excel文件,可以发送一个GET或POST请求,并设置响应类型为blob或arraybuffer,然后使用new Blob()构造函数创建一个二进制文件,最后使用URL.createObjectURL()生成一个可以下载的链接。 先看代码 import…...
LLM与NLP
大语言模型与自然语言处理的关系:整体与组成的关系如 自然语言理解的编码器式(encoder-only)的架构是语境相关的词表示BERT; 自然语言转换的编码器-解码器式的(encoder-decoder)的架构是词频-逆文档词频T…...
js 判断是否为回文串
需求:忽略英文大小写和空格差异,判断是否为回文字符串(例如"我爱你 你爱我","abc bA") 思路:利用翻转字符串比较,利用循环双指针,利用递归或者双循环…...
多重背包c++
题目描述 有N种物品和一个容量是V的背包。 第i种物品最多有si件,每件体积是vi,价值是wi。 求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。 输出最大价值。 输入 第一行两个整数,N&#x…...
kernel input事件测试程序
测试内核input 事件测试程序。 getevent -lt 命令查看注册的是是event0/1/2/3/4 中的哪一个。 gcc input_test.c -o input_test 编译成可执行程序。将编译的input_test,U盘或ADB push到系统里面,chmod 777 input_test 在 ./input_test input_test.c #…...
gd32 i2c 中断 主机从机双向通信例程
Master I2C0_SCL PB8 AF4 I2C0_SDA PB9 AF4 Slave I2C1_SCL PB10 AF4 I2C1_SDA PB11 AF4 //主机中断发送 void i2c_master_transmit_it(uint32_t address, uint8_t* buff, uint32_t size); //主机中断接收 void i2c_master_receive_it(uint32_t address, uint8_t* buff, uint…...
程序员在AI时代:重塑核心竞争力,共舞智能未来
程序员在AI时代:重塑核心竞争力,共舞智能未来 在这个日新月异的科技时代,人工智能生成内容(AIGC)技术,尤其是以ChatGPT、Midjourney、Claude等为代表的大语言模型,正以前所未有的速度渗透到编程…...
apex发送邮件中显示饼状图和条形图
在 Apex 中发送带有嵌入图表(如饼状图和条形图)的电子邮件,您可以通过以下步骤实现: 生成图表图像:使用外部库或服务生成图表图像并获取图像的 URL 或 Base64 编码。创建电子邮件模板:在 HTML 邮件模板中嵌…...

【HarmonyOS NEXT星河版开发学习】小型测试案例07-弹性布局小练习
个人主页→VON 收录专栏→鸿蒙开发小型案例总结 基础语法部分会发布于github 和 gitee上面(暂未发布) 前言 在鸿蒙(HarmonyOS)开发中,Flex布局是一种非常有用的布局方式,它允许开发者创建灵活且响…...
Sparksql array相关函数
前言 Apache Spark SQL 是 Spark 的一个重要模块,用于处理结构化数据。它提供了 DataFrame 和 Dataset API,使得开发者能够使用 SQL 查询语言(称为 Spark SQL)对数据进行高效的操作。在本文中,我们将介绍 Spark SQL 中所有与array相关的函数。 环境 sparksql版本<dep…...

软件测试学习笔记
测试学习 1. 测试流程2. Bug的提出什么是bugbug 的描述bug 级别 3. 测试用例的设计什么是测试用例测试用例应如何设计基于需求的设计方法等价类边界值场景法正交表法判定表法错误猜测法 4. 自动化测试回归测试自动化分类 5. 安装 webdriver-manager 和 selenium第一个web自动化…...
Centos 8系统ext4文件系统类型进行扩容缩容 (LVM)
Centos 8系统ext4文件系统类型进行扩容缩容 (LVM) 1.磁盘情况:2.缩容home分区1.备份home数据:2.查找使用 /home 的进程:3.终止这些进程:4.卸载 /home 分区5.检查文件系统一致性 (e2fsck):6.调整…...
常考常考高频率
1.快排(双指针) 快排,归并排序,堆排序 #快速排序O(nlogn) def quick_sort(array, left, right):if left < right:mid partition(array, left, right)quick_sort(array, left, mid)quick_sort(array, …...

Linux项目环境的搭建 (Red hat 9.0Linux操作系统)
一、目的: 1.搭建Linux操作系统项目所需的项目环境构件; 2.了解 Linux的组成,学会编译内核。 二、内容: 安装Red hat 9.0Linux操作系统; 三、步骤: 3.1 正确安装Redhat9.0操作系统。 3.2 rpm -Uvh *.…...

Study--Oracle-08-ORACLE数据备份与恢复(一)
一、ORACLE数据保护方案 1、oracle数据保护方案 2、数据库物理保护方案 oracle数据库备份可以备份到本地集群存储,也可以备份到云存储。 3、数据库逻辑数据保护方案 二、ORACLE数据体系 1、ORACLE 数据库的存储结构 2、oracle物理和逻辑存储结构 3、数据库进程 4、数据库日…...

FreeIPA安装
一、环境准备 主机名IP角色master. bhlu. com192.168.22.10服务端node1. bhlu. com192.168.22.11客户端 两台服务器关闭防火墙和 selinux配置好 yum 源 1.1 配置 chronyd 配置好 chronyd,使用 chronyc source -v 可以验证 # 这里写了一个playbook作为示例了 --…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...

c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...