news 2026/5/16 6:55:05

SQLSugar 学习笔记

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SQLSugar 学习笔记

一、核心概念

术语说明
ORMObject Relational Mapping,对象关系映射。把 C# 类和数据库表建立映射,开发者主要操作对象而不是手写大量 SQL。
SqlSugar.NET 开源 ORM 框架,支持 .NET Framework、.NET Core、.NET 5/6/7/8/9 等环境,常用于快速完成数据库增删改查。
SqlSugarClientSQLSugar 原生客户端对象。推荐每次请求或每个业务上下文创建新对象,禁止作为全局单例跨线程复用。
SqlSugarScopeSQLSugar 单例模式客户端对象。通过AsyncLocal管理上下文,对新手更友好,但遇到Task.WhenAllParallel、后台任务等场景仍需注意线程安全。
ConnectionConfig数据库连接配置对象,用于配置连接字符串、数据库类型、是否自动关闭连接等信息。
DbType数据库类型枚举,例如DbType.SqlServerDbType.MySqlDbType.Sqlite等。
IsAutoCloseConnection是否自动关闭数据库连接。设置为true后,一般不需要手动关闭连接。
SugarTable实体类映射特性,用于指定 C# 类对应的数据库表名。
SugarColumn实体属性映射特性,用于指定字段、主键、自增、忽略列、列名等配置。
CodeFirst根据实体类自动创建或更新数据库表结构,适合学习、快速原型和内部工具开发。
DbMaintenance数据库维护 API,可用于创建数据库、判断表是否存在等数据库级操作。
Queryable查询入口,用于构建查询条件、排序、分页、联表查询并返回数据。
Insertable新增入口,用于插入单个对象或集合。
Updateable修改入口,用于根据实体或条件更新数据。
Deleteable删除入口,用于根据实体或条件删除数据。
Expressionable动态表达式构建器,适合根据界面输入拼接可选查询条件。
AOP 日志通过db.Aop.OnLogExecuting等事件查看 ORM 生成的 SQL,常用于调试和性能分析。
ADO.NET.NET 原生数据库访问技术,核心对象包括SqlConnectionSqlCommandSqlDataReaderSqlDataAdapterDataSet等。SQLSugar 底层仍然依赖 ADO.NET。

1. SQLSugar 与 ADO.NET 的关系

ADO.NET 需要开发者手动管理连接、命令、参数、结果集转换等细节,代码量较大。SQLSugar 在 ADO.NET 之上封装了对象映射和链式 API,使常见数据库操作更接近业务对象操作。

对比项ADO.NETSQLSugar
开发方式手写 SQL 和参数,手动读取结果集链式 API + Lambda 表达式
实体转换需要手动转换DataTableDataReader自动映射成实体对象
查询条件拼接 SQL 或构造参数WhereWhereIFExpressionable
分页手写分页 SQLToPageList
适用场景对 SQL 控制要求极高、老项目维护快速开发、业务系统、后台管理系统

2. SqlSugarClient 与 SqlSugarScope 的选择

模式使用方式适用场景注意事项
SqlSugarClient每次请求或每个上下文new一个对象WinForm 小项目、控制台、普通业务方法不要做全局单例;跨线程或跨请求共用容易出偶发问题。
SqlSugarScope作为单例注入或全局对象Web 项目、统一封装数据库访问层不要反复new SqlSugarScope;并行任务中必要时使用db.CopyNew()

二、常用操作

1. 安装与项目依赖

当前示例项目是.NET Framework 4.7.2WinForm 项目,packages.config中包含:

包名示例版本作用
SqlSugar5.1.4.207SQLSugar ORM 主包
Newtonsoft.Json13.0.4SQLSugar 部分功能或序列化场景可能依赖
<packages> <package id="Newtonsoft.Json" version="13.0.4" targetFramework="net472" /> <package id="SqlSugar" version="5.1.4.207" targetFramework="net472" /> </packages>

2. 创建数据库连接对象

操作常用 API说明
创建客户端new SqlSugarClient(config)创建数据库访问对象。
配置连接字符串ConnectionString指定服务器、数据库、账号、密码等。
指定数据库类型DbType.SqlServer根据实际数据库选择类型。
自动关闭连接IsAutoCloseConnection = true推荐开启,减少手动管理连接的负担。
查看执行 SQLAop.OnLogExecuting调试时查看 ORM 生成的 SQL。
using SqlSugar; ​ SqlSugarClient db = new SqlSugarClient(new ConnectionConfig { ConnectionString = "server=.;database=sugarDemo;uid=sa;pwd=123456;", DbType = DbType.SqlServer, IsAutoCloseConnection = true }); ​ db.Aop.OnLogExecuting = (sql, pars) => { Console.WriteLine(UtilMethods.GetNativeSql(sql, pars)); };

3. 实体类与表映射

操作常用 API说明
指定表名[SugarTable("Student")]类名和表名不一致时使用。
指定主键[SugarColumn(IsPrimaryKey = true)]标记主键字段。
指定自增[SugarColumn(IsIdentity = true)]标记数据库自增列。
可空字段int?DateTime?C# 可空类型对应数据库可空字段。
using SqlSugar; ​ [SugarTable("Student")] public class Student { [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] public int Id { get; set; } ​ public string Name { get; set; } ​ public int? Age { get; set; } }

4. 创建数据库与表

操作常用 API说明
创建数据库DbMaintenance.CreateDatabase()如果数据库不存在则创建。
根据实体建表CodeFirst.InitTables<T>()根据实体类创建或更新表结构。
多表初始化CodeFirst.InitTables(typeof(A), typeof(B))多个实体一次初始化。
bool created = db.DbMaintenance.CreateDatabase(); ​ if (created) { Console.WriteLine("数据库创建成功"); } ​ db.CodeFirst.InitTables<Student>();

5. 新增数据

操作常用 API说明
插入单条Insertable(entity).ExecuteCommand()返回影响行数。
插入多条Insertable(list).ExecuteCommand()批量插入集合。
返回自增 IDExecuteReturnIdentity()插入后获取自增主键。
Student student = new Student { Name = "张三", Age = 20 }; ​ int rows = db.Insertable(student).ExecuteCommand(); List<Student> students = new List<Student> { new Student { Name = "张三", Age = 20 }, new Student { Name = "李四", Age = 21 }, new Student { Name = "王五", Age = 22 } }; ​ int rows = db.Insertable(students).ExecuteCommand();

6. 查询数据

操作常用 API说明
查询全部Queryable<T>().ToList()返回所有记录。
条件查询Where(predicate)使用 Lambda 表达式筛选。
查询单条Single(predicate)没有数据返回null,多条数据会抛异常。
查询第一条First(predicate)返回第一条匹配数据。
统计数量Count()返回记录数量。
模糊查询Contains()通常会转换为 SQL 的LIKE
List<Student> list = db.Queryable<Student>() .Where(s => s.Age > 18) .ToList(); ​ Student student = db.Queryable<Student>() .Single(s => s.Id == 1); ​ int count = db.Queryable<Student>() .Where(s => s.Name.Contains("张")) .Count();

7. 动态条件查询

动态查询适合 WinForm 查询面板、后台列表筛选等场景。用户没有填写条件时不拼接,填写后才追加筛选条件。

操作常用 API说明
创建表达式Expressionable.Create<T>()创建动态表达式对象。
条件成立才 AndAndIF(condition, expression)condition为 true 才追加。
条件成立才 OrOrIF(condition, expression)condition为 true 才追加。
转换表达式ToExpression()交给Where使用。
string keyword = textBoxName.Text.Trim(); string ageText = textBoxAge.Text.Trim(); ​ var exp = Expressionable.Create<Student>(); ​ exp = exp.AndIF(!string.IsNullOrWhiteSpace(keyword), s => s.Name.Contains(keyword)); ​ exp = exp.AndIF(int.TryParse(ageText, out int age), s => s.Age > age); ​ List<Student> list = db.Queryable<Student>() .Where(exp.ToExpression()) .ToList();

8. 分页查询

操作常用 API说明
分页查询ToPageList(page, pageSize, ref totalCount, ref totalPage)返回当前页数据,同时输出总条数和总页数。
当前页page一般从 1 开始。
每页条数pageSize可由下拉框选择。
总条数totalCount查询结果总记录数。
总页数totalPage根据总条数和每页条数计算。
int page = 1; int pageSize = 10; int totalCount = 0; int totalPage = 0; ​ List<Student> list = db.Queryable<Student>() .Where(s => s.Age > 18) .ToPageList(page, pageSize, ref totalCount, ref totalPage);

WinForm 分页按钮常见逻辑:

void FirstPage() { page = 1; BindDataGridView(); } ​ void PreviousPage() { if (page > 1) { page--; BindDataGridView(); } } ​ void NextPage() { if (page < totalPage) { page++; BindDataGridView(); } } ​ void LastPage() { page = totalPage; BindDataGridView(); }

9. 修改数据

操作常用 API说明
根据实体修改Updateable(entity).ExecuteCommand()根据主键更新实体字段。
先查后改Queryable<T>().Single()+Updateable()适合编辑窗口回显再保存。
指定列更新UpdateColumns(...)只更新部分字段,避免误改。
条件更新Where(...)指定更新范围。
Student student = db.Queryable<Student>() .Single(s => s.Id == 1); ​ student.Name = "新姓名"; student.Age = 25; ​ int rows = db.Updateable(student).ExecuteCommand(); int rows = db.Updateable<Student>() .SetColumns(s => new Student { Age = 30 }) .Where(s => s.Id == 1) .ExecuteCommand();

10. 删除数据

操作常用 API说明
根据实体删除Deleteable(entity).ExecuteCommand()根据实体主键删除。
根据主键删除Deleteable<T>().In(id)删除指定主键。
条件删除Deleteable<T>().Where(...)删除满足条件的数据。
逻辑删除Updateable<T>().SetColumns(...)实际项目更常用,把状态改为禁用或删除。
Student student = db.Queryable<Student>() .Single(s => s.Id == 1); int rows = db.Deleteable(student).ExecuteCommand(); int rows = db.Deleteable<Student>() .Where(s => s.Age < 18) .ExecuteCommand();

业务系统中建议优先使用逻辑删除:

int rows = db.Updateable<Student>() .SetColumns(s => new Student { Age = null }) .Where(s => s.Id == 1) .ExecuteCommand();

11. 在 N 层架构中的使用思路

仓库温控系统示例采用了ModelIDALDALDALFactoryBLLUI的分层思路。该示例主要使用传统 ADO.NET,但其架构思想可以迁移到 SQLSugar。

层级职责使用 SQLSugar 时的建议
Model定义实体类,映射表结构给实体添加SugarTableSugarColumn
IDAL定义数据访问接口定义通用 CRUD、分页、业务查询接口。
DAL实现数据库访问使用 SQLSugar 的QueryableInsertable等 API。
BLL处理业务逻辑调用 DAL,不直接操作数据库。
UI窗体或页面展示调用 BLL,绑定DataGridView或页面列表。

简单封装示例:

public class StudentDal { private readonly SqlSugarClient db; public StudentDal(SqlSugarClient db) { this.db = db; } public List<Student> GetPage(string keyword, int page, int pageSize, ref int totalCount, ref int totalPage) { return db.Queryable<Student>() .WhereIF(!string.IsNullOrWhiteSpace(keyword), s => s.Name.Contains(keyword)) .ToPageList(page, pageSize, ref totalCount, ref totalPage); } public int Add(Student model) { return db.Insertable(model).ExecuteCommand(); } public int Update(Student model) { return db.Updateable(model).ExecuteCommand(); } public int Delete(int id) { return db.Deleteable<Student>().In(id).ExecuteCommand(); } }

三、问题排查

错误1:CodeFirst 初始化时报 Newtonsoft.Json 相关异常

  • 现象:执行db.CodeFirst.InitTables<Student>()时提示缺少Newtonsoft.Json程序集,或出现程序集加载失败。

  • 原因:项目未安装Newtonsoft.Json,或者版本与当前 SQLSugar 依赖不匹配。

  • 解决:在项目中安装Newtonsoft.Json,示例项目使用版本13.0.4。如果是packages.config项目,需要确认引用路径和packages目录存在。

错误2:SqlSugarClient 被全局复用后出现偶发异常

  • 现象:多线程、多个窗体或多个请求同时操作数据库时,偶尔出现连接状态异常、上下文混乱或查询结果异常。

  • 原因SqlSugarClient不建议作为全局单例跨上下文复用。它适合每次请求或每个业务上下文创建新对象。

  • 解决:普通模式下每个业务上下文new SqlSugarClient;Web 项目可用 IOC 的 Scope 或瞬时生命周期;如必须复制上下文,可使用db.CopyNew()

错误3:SqlSugarScope 被反复 new 导致内存问题

  • 现象:使用SqlSugarScope后,程序运行时间越长内存越高。

  • 原因SqlSugarScope设计上适合单例,不应该在每个方法中一直创建新对象。

  • 解决:将SqlSugarScope注册为单例或统一静态对象;并行任务、后台任务中根据官方建议使用CopyNew()处理特殊线程场景。

错误4:Single 查询多条数据时报错

  • 现象:调用db.Queryable<Student>().Single(s => s.Name == "张三")抛出异常。

  • 原因Single语义要求结果最多一条;如果数据库中存在多条匹配数据会报错。

  • 解决:确认查询条件能唯一定位数据,例如主键查询;如果只需要第一条,改用First;如果需要多条,改用ToList

Student byId = db.Queryable<Student>().Single(s => s.Id == 1); Student first = db.Queryable<Student>().First(s => s.Name == "张三"); List<Student> list = db.Queryable<Student>().Where(s => s.Name == "张三").ToList();

错误5:动态查询中 int.Parse 导致格式异常

  • 现象:年龄输入框为空或输入非数字时,int.Parse(textBoxAge.Text)抛出FormatException

  • 原因:用户输入没有经过格式校验。

  • 解决:使用int.TryParse,只有转换成功时才拼接年龄条件。

var exp = Expressionable.Create<Student>(); if (int.TryParse(textBoxAge.Text.Trim(), out int age)) { exp = exp.And(s => s.Age > age); } List<Student> list = db.Queryable<Student>() .Where(exp.ToExpression()) .ToList();

错误6:手动拼接 SQL 存在注入风险

  • 现象:登录、查询等功能使用字符串拼接条件,例如把用户输入直接拼到 SQL 条件中。

  • 原因:未使用参数化查询或 ORM 表达式,恶意输入可能改变 SQL 语义。

  • 解决:使用 SQLSugar 的 Lambda 查询或参数化 API,避免直接拼接用户输入。

User user = db.Queryable<User>() .Where(u => u.Account == account && u.Password == password && u.Status == 0) .First();

错误7:分页后页码显示异常

  • 现象:删除数据或修改每页条数后,当前页大于总页数,列表为空或显示5/4

  • 原因:数据总量变化后没有修正当前页。

  • 解决:重新查询后判断totalPage,若当前页超过总页数则回到最后一页或第一页。

if (page > totalPage) { page = totalPage <= 0 ? 1 : totalPage; }

四、相关资源

  • 官方文档:https://www.donet5.com/home/doc

  • 线程安全说明:https://www.donet5.com/Home/Doc?typeId=1224

  • NuGet 包:https://www.nuget.org/packages/SqlSugarCore

  • ORM 对比参考:https://segmentfault.com/a/1190000011676744

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/16 6:54:04

3D打印卡扣式外壳设计:为Feather RP2040 DVI开发板打造专属保护

1. 项目概述&#xff1a;为你的图形化微控制器找个“家”在嵌入式硬件开发里&#xff0c;尤其是涉及到图形界面输出的项目&#xff0c;我们常常会面临一个现实问题&#xff1a;如何优雅地安放和保护那块裸露的开发板&#xff1f;直接摆在桌面上&#xff0c;不仅容易积灰、短路&…

作者头像 李华
网站建设 2026/5/16 6:54:00

基于CPython与Adafruit IO的物联网网关实战:串口通信与云端数据桥接

1. 项目概述与核心价值 最近在折腾一个智能灯光的小项目&#xff0c;核心是想让一串NeoPixels灯带能根据云端发送的指令实时改变颜色。听起来简单&#xff0c;但要把嵌入式设备、本地脚本和物联网云平台这三者无缝衔接起来&#xff0c;里面有不少门道。我选择了Adafruit IO作为…

作者头像 李华
网站建设 2026/5/16 6:53:49

上下文膨胀终结者:正文写文件脱钩架构——将3章上下文从42500压至5850

上下文膨胀终结者:正文写文件脱钩架构——将3章上下文从42500压至5850 本文收录于《工程化AI人机协同方法论》系列专栏,对应系列第63篇核心文章 核心结论前置:多智能体架构的最大性能瓶颈,从来不是Task调用次数,而是正文回流主上下文。所有将生成的正文堆在主对话上下文的…

作者头像 李华
网站建设 2026/5/16 6:50:40

基于Python与Whisper的Reddit视频自动化抓取与字幕生成方案

1. 项目概述与核心价值最近在GitHub上看到一个挺有意思的项目&#xff0c;叫rusiaaman/wcgw。光看这个名字&#xff0c;可能有点摸不着头脑&#xff0c;但如果你经常混迹于Reddit的r/Whatcouldgowrong板块&#xff0c;或者对网络上的各种“翻车”集锦情有独钟&#xff0c;那这个…

作者头像 李华
网站建设 2026/5/16 6:50:37

AMTP协议与OpenClaw实现:复杂网络下的大文件可靠传输方案

1. 项目概述&#xff1a;从协议到实现&#xff0c;理解AMTP与OpenClaw的协同最近在梳理一些私有化部署和跨网络数据同步的方案时&#xff0c;又翻到了AMTP&#xff08;Advanced Message Transfer Protocol&#xff09;这个协议&#xff0c;以及围绕它构建的amtp-openclaw这个开…

作者头像 李华
网站建设 2026/5/16 6:47:10

终极免费视频下载解决方案:Parabolic让你轻松获取200+平台内容

终极免费视频下载解决方案&#xff1a;Parabolic让你轻松获取200平台内容 【免费下载链接】Parabolic Download web video and audio 项目地址: https://gitcode.com/GitHub_Trending/pa/Parabolic 还在为下载在线视频而烦恼吗&#xff1f;面对复杂的命令行工具和功能单…

作者头像 李华