当前位置: 首页 > news >正文

SqlSugar + SQLite ORM入门:从零搭建.NET数据访问层与CRUD实战

1. 为什么选择SqlSugar + SQLite作为你的第一个ORM项目?

如果你刚开始接触后端开发,或者想给自己的桌面应用、移动端App加一个轻量级的本地数据存储,那么“数据库”这个词可能会让你感到既熟悉又陌生。熟悉是因为它无处不在,陌生是因为从写SQL语句到真正在代码里把数据存进去、查出来,中间好像隔着一道鸿沟。直接拼装SQL字符串?太容易出错,而且代码又臭又长。这时候,ORM(对象关系映射)工具就像一位翻译官,帮你把面向对象的代码(C#里的类)和关系型数据库的表自动对应起来,让你能用操作“对象”的方式去操作“数据”。

在.NET生态里,SqlSugar是近年来非常热门的一个国产ORM框架。它轻量、高性能、语法糖丰富(这也是它名字的由来),对开发者非常友好。而SQLite,则是一个无需安装、配置、零管理的嵌入式数据库,一个.db文件就是整个数据库,完美契合开发测试、单机应用、移动端等场景。把这两者结合起来,你几乎可以零成本地搭建一个可用的数据访问层,快速验证你的业务逻辑。这比一上来就折腾SQL Server、MySQL的安装和远程连接要友好得多。我见过很多新手项目死在环境配置上,而SqlSugar+SQLite这个组合,能让你跳过那些坑,直接专注于业务代码本身,体验从零到一建立起数据持久化能力的完整过程,这种正反馈对学习至关重要。

2. 项目起手式:环境搭建与第一个DbContext

理论说再多不如动手。我们首先需要一个.NET项目。这里以主流的.NET 6/8的控制台应用为例,当然,你也可以用在ASP.NET Core Web API、WPF、MAUI等项目里,原理相通。

第一步:创建项目与安装NuGet包打开你的IDE(Visual Studio, VS Code, Rider等),创建一个新的控制台应用项目。然后,通过NuGet包管理器安装以下两个核心包:

Install-Package SqlSugarCore Install-Package System.Data.SQLite.Core

第一个是SqlSugar本体,第二个是SQLite的ADO.NET数据提供程序,SqlSugar需要通过它来和SQLite.db文件对话。

第二步:配置并初始化SqlSugar的DbContext在ORM的世界里,DbContext(数据库上下文)是你的指挥中心,它管理着数据库连接、事务,以及所有实体类的映射关系。我们来创建一个:

using SqlSugar; using System; namespace SqlSugarDemo { public class SqliteDbContext { // SqlSugarClient 是操作数据库的主要入口 public SqlSugarClient Db { get; } public SqliteDbContext() { // 1. 配置连接字符串 // Data Source 指定数据库文件路径。这里使用相对路径,会在程序运行目录下创建 test.db 文件 // 如果文件不存在,SQLite会自动创建它。 string connectionString = "Data Source=./test.db"; // 2. 创建并配置 SqlSugarClient Db = new SqlSugarClient(new ConnectionConfig() { ConnectionString = connectionString, // 连接字符串 DbType = DbType.Sqlite, // 数据库类型 IsAutoCloseConnection = true, // 是否自动关闭连接(建议true,由框架管理) InitKeyType = InitKeyType.Attribute // 主键、自增等如何生成,这里使用特性(Attribute)标注 }); // 3. (可选但推荐) 输出SqlSugar生成的SQL语句到控制台,方便调试和学习 Db.Aop.OnLogExecuting = (sql, pars) => { Console.WriteLine($"【SQL语句】{sql}"); // 如果需要打印参数,可以遍历 pars }; } } }

这段代码有几个关键点值得展开:

  • 连接字符串Data Source=./test.db是最简单的形式。你可以用绝对路径(如C:\mydata.db),也可以用:memory:来创建一个仅存在于内存中的临时数据库,非常适合单元测试。
  • IsAutoCloseConnection:设为true是推荐做法。SqlSugar会在每次数据库操作(查询、插入等)后,自动判断并关闭连接,避免了传统ADO.NET中需要手动Open()Close()的繁琐,也减少了连接泄露的风险。
  • InitKeyType:设为Attribute意味着我们将在实体类上用[SugarColumn]等特性来详细定义字段属性。另一种方式是InitKeyType.SystemTable,通过读取数据库现有表结构来反向生成实体信息,但在代码先行(Code First)的开发模式中,使用特性更直观、可控。
  • Aop.OnLogExecuting:这是一个神器。它会在SqlSugar执行SQL前触发,把生成的原始SQL和参数打印出来。对于初学者,这是理解ORM如何工作的“透视镜”;对于老手,这是排查慢查询、验证逻辑的调试利器。

3. 定义你的数据蓝图:实体类与特性映射

有了指挥中心,我们需要定义“士兵”——也就是实体类。它对应数据库里的一张表。假设我们要做一个简单的用户管理系统,先定义一个User类。

using SqlSugar; namespace SqlSugarDemo.Entities { [SugarTable("Users")] // 指定该实体类映射到数据库中的表名,如果不加,默认用类名 public class User { // SqlColumn是字段特性,IsPrimaryKey代表主键,IsIdentity代表自增(对于SQLite,通常是整数主键自动自增) [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] public int Id { get; set; } [SugarColumn(ColumnName = "Name")] public string Name { get; set; } [SugarColumn(ColumnName = "Age")] public int Age { get; set; } [SugarColumn(ColumnName = "Email", IsNullable = true)] // IsNullable = true 表示字段可空 public string? Email { get; set; } [SugarColumn(ColumnName = "CreatedAt")] public DateTime CreatedAt { get; set; } } }

这里详细拆解一下[SugarColumn]特性的常用属性:

  • ColumnName:指定实体属性对应数据库表的哪个字段。如果属性名和字段名一致,可以省略。
  • IsPrimaryKey:是否为主键。一个表必须有一个主键。
  • IsIdentity:是否为自增列。对于SQLite,通常将整数主键设为自增,插入时不用管Id,数据库会自动分配。
  • IsNullable:字段是否允许为NULL。对于引用类型(如string),默认就是可空的(C# 8.0+的可空引用类型上下文下),但显式声明是个好习惯。值类型(如int)需要设置为IsNullable = true才能在数据库里存NULL。
  • ColumnDataType:可以强制指定字段在数据库中的类型,如ColumnDataType = "VARCHAR(50)"。但SqlSugar通常能根据C#类型智能推断,除非有特殊需求。

踩坑点1:SQLite的“伪自增”很多人以为在SQLite里,把主键设为INTEGER PRIMARY KEY就会自动自增。实际上,如果你在插入时显式指定了该列的值(即使为0或NULL),SQLite会尝试使用你提供的值,可能导致主键冲突。而SqlSugar的IsIdentity=true会在插入时主动忽略这个属性的值,确保由数据库分配ID,这才是真正的“自增”行为。所以,在定义实体时,明确标出IsIdentity很重要。

踩坑点2:字符串长度与索引SQLite对VARCHAR长度限制比较宽松,但如果你知道某个字段(如用户名、邮箱)有最大长度,最好用ColumnDataTypeLength属性([SugarColumn(Length=100)])进行限定。这不仅是数据完整性的要求,更重要的是,如果你未来要为这个字段创建索引,不定长度的字段可能会影响索引效率和查询性能。虽然入门阶段可能用不到,但养成好习惯能避免后期重构。

4. 从创建表到增删改查:完整的CRUD实战

DbContext和实体类都准备好了,现在让我们把它们用起来。我们在Program.csMain方法中编写以下代码。

4.1 创建数据库表(Code First)

Code First模式允许我们通过代码来创建或更新表结构。

using SqlSugarDemo; using SqlSugarDemo.Entities; var dbContext = new SqliteDbContext(); // 创建数据库表(如果不存在) dbContext.Db.CodeFirst.InitTables(typeof(User)); Console.WriteLine("表‘Users’已就绪。");

InitTables方法会检查当前数据库,如果Users表不存在,则根据User实体类的定义创建它;如果存在,但结构不一致(比如新增了字段),它会尝试修改表结构(添加新列)。注意:对于已有数据的表,修改列类型或删除列等危险操作,SqlSugar默认是关闭的,需要额外配置,生产环境务必谨慎。

4.2 插入数据(Create)

插入单条数据非常简单:

var newUser = new User { Name = "张三", Age = 25, Email = "zhangsan@example.com", CreatedAt = DateTime.Now }; // Insertable 返回一个插入构建器,ExecuteReturnIdentity 执行插入并返回自增的主键Id var userId = dbContext.Db.Insertable(newUser).ExecuteReturnIdentity(); Console.WriteLine($"插入成功,用户Id: {userId}");

批量插入能极大提升性能:

var userList = new List<User> { new User { Name = "李四", Age = 30, CreatedAt = DateTime.Now }, new User { Name = "王五", Age = 28, Email = "wangwu@example.com", CreatedAt = DateTime.Now }, }; // ExecuteReturnIdentity 在批量插入时返回的是第一个插入行的Id,不是列表。 var affectedRows = dbContext.Db.Insertable(userList).ExecuteCommand(); Console.WriteLine($"批量插入成功,影响行数: {affectedRows}");

4.3 查询数据(Read)

查询是ORM最核心也最丰富的功能。SqlSugar提供了接近LINQ的链式查询语法,非常直观。

(1)查询所有记录

var allUsers = dbContext.Db.Queryable<User>().ToList(); foreach (var user in allUsers) { Console.WriteLine($"ID:{user.Id}, 姓名:{user.Name}, 年龄:{user.Age}"); }

Queryable<T>()是查询的起点,ToList()执行查询并将结果映射到List<User>

(2)带条件的查询(Where)

// 查询年龄大于25岁的用户 var adultUsers = dbContext.Db.Queryable<User>() .Where(u => u.Age > 25) .ToList(); // 查询邮箱不为空的用户,并按年龄倒序排列 var usersWithEmail = dbContext.Db.Queryable<User>() .Where(u => u.Email != null) .OrderBy(u => u.Age, OrderByType.Desc) // OrderByType.Desc 表示降序 .ToList();

这里的WhereOrderBy用法和LINQ几乎一样,SqlSugar会将其转换为对应的SQLWHEREORDER BY子句。打开之前配置的SQL输出,你就能看到生成的语句。

(3)分页查询分页是Web应用中最常见的需求。

int pageIndex = 1; // 第1页 int pageSize = 10; // 每页10条 RefAsync<int> totalCount = 0; // 用于接收总记录数 var pagedList = await dbContext.Db.Queryable<User>() .ToPageListAsync(pageIndex, pageSize, totalCount); // 同步方法:.ToPageList(pageIndex, pageSize, ref totalCount) Console.WriteLine($"第{pageIndex}页数据,共{totalCount}条记录,每页{pageSize}条。"); foreach (var user in pagedList) { Console.WriteLine(user.Name); }

ToPageListAsync方法会执行两次查询:一次是COUNT(*)获取总数,另一次是LIMIT ... OFFSET ...获取当前页数据。这对于大数据量表是必要的开销。

(4)查询单个字段或简单统计

// 查询所有用户的名字列表 var names = dbContext.Db.Queryable<User>().Select(u => u.Name).ToList(); // 查询用户平均年龄 var avgAge = dbContext.Db.Queryable<User>().Avg(u => u.Age); // 类似的还有 .Sum(), .Max(), .Min(), .Count()

4.4 更新数据(Update)

更新操作需要指定条件,否则会变成全表更新(极其危险!SqlSugar默认有保护机制,但自己也要小心)。

// 更新指定Id的用户信息 var updateResult = dbContext.Db.Updateable<User>() .SetColumns(u => new User() { Name = "张老三", Email = "newemail@example.com" }) .Where(u => u.Id == userId) .ExecuteCommand(); // 另一种更简洁的写法:根据实体对象的主键值进行更新 var userToUpdate = dbContext.Db.Queryable<User>().First(u => u.Id == userId); if (userToUpdate != null) { userToUpdate.Age = 26; userToUpdate.Email = "updated@example.com"; dbContext.Db.Updateable(userToUpdate).ExecuteCommand(); }

SetColumns方法允许你只更新指定的列,这对于只需要修改部分字段的场景非常高效,能避免全字段更新带来的并发问题和不必要的网络传输。

4.5 删除数据(Delete)

删除操作更要慎之又慎,务必确保Where条件准确。

// 删除指定Id的用户 var deleteResult = dbContext.Db.Deleteable<User>() .Where(u => u.Id == someId) .ExecuteCommand(); // 根据主键值直接删除 dbContext.Db.Deleteable<User>().In(1).ExecuteCommand(); // 删除Id为1的记录 dbContext.Db.Deleteable<User>().In(new[]{1,2,3}).ExecuteCommand(); // 删除Id为1,2,3的记录

In方法对于按主键批量删除非常方便。

5. 进阶技巧与实战避坑指南

掌握了基本的CRUD,你已经可以应付大多数简单场景。但要想写出更健壮、高效的代码,还需要了解下面这些进阶知识和常见陷阱。

5.1 事务处理:保证数据一致性

事务用于确保一系列数据库操作要么全部成功,要么全部失败。比如转账操作:A账户扣钱和B账户加钱必须同时成功。

try { dbContext.Db.Ado.BeginTran(); // 开始事务 // 操作1:A账户扣款 dbContext.Db.Updateable<Account>().SetColumns(a => a.Balance - 100).Where(a => a.Id == 1).ExecuteCommand(); // 模拟一个可能失败的操作 // int a = 0; // int b = 1 / a; // 这里会抛出异常,触发回滚 // 操作2:B账户收款 dbContext.Db.Updateable<Account>().SetColumns(a => a.Balance + 100).Where(a => a.Id == 2).ExecuteCommand(); dbContext.Db.Ado.CommitTran(); // 提交事务 Console.WriteLine("事务执行成功!"); } catch (Exception ex) { dbContext.Db.Ado.RollbackTran(); // 回滚事务 Console.WriteLine($"事务执行失败,已回滚。错误:{ex.Message}"); }

关键点:务必使用try-catch包裹事务代码,在catch中执行回滚。CommitTran只有在所有操作都成功后才会被调用。SqlSugar也支持更简洁的UseTran方法,它自动处理提交和回滚。

5.2 实体类与数据库的同步策略

在项目初期,表结构变动频繁。除了手动调用CodeFirst.InitTables,你还可以配置更智能的同步策略。在创建SqlSugarClient时进行配置:

var db = new SqlSugarClient(new ConnectionConfig(){...}); // 配置实体同步策略 db.CodeFirst .SetStringDefaultLength(200) // 设置string类型字段的默认长度 .BackupTable().InitTables(typeof(User), typeof(Product)); // 修改表时备份数据

BackupTable()是一个很实用的方法。当表结构发生变化(如添加新列)时,SqlSugar会先备份原表数据,然后创建新表,最后把数据导回去。这比直接ALTER TABLE在某些复杂变更上更安全。但对于删除列、修改列类型等操作,仍需评估数据丢失风险。

5.3 性能优化初探

  • 异步操作:尽可能使用*Async方法(如ToListAsync,ExecuteCommandAsync),特别是在Web应用中,可以避免阻塞线程,提高并发能力。
  • 批量操作:如前所述,插入、更新、删除都支持批量操作,能减少数据库往返次数,性能提升一个数量级。
  • 查询优化
    • 只查询需要的字段:使用.Select(u => new { u.Id, u.Name })而不是查询整个实体,尤其是在表字段很多或包含大文本字段时。
    • 合理使用索引:虽然ORM层面不直接创建索引,但你要知道,在WhereOrderByGroupBy中频繁使用的字段,应该在数据库层面建立索引。对于SQLite,你可以在实体类特性中标注[SugarColumn(IsIndex = true)],然后在InitTables时让SqlSugar帮你创建,或者手动执行SQL:CREATE INDEX IX_Users_Age ON Users(Age);
    • 警惕N+1查询问题:这是一个ORM通病。例如,你先查询一个用户列表,然后遍历每个用户去查询他的订单详情,就会产生1次查询用户 + N次查询订单的N+1次查询。解决方法是使用联表查询导航属性(SqlSugar支持[Navigate]特性)。

5.4 联表查询入门

假设我们还有一个Order表,通过UserId关联User表。SqlSugar的联表查询非常强大。

// 假设有Order实体,包含UserId字段 var list = dbContext.Db.Queryable<User>() .LeftJoin<Order>((u, o) => u.Id == o.UserId) // 左连接 .Where((u, o) => o.Amount > 100) // 联表后的条件 .Select((u, o) => new // 选择需要返回的字段 { UserId = u.Id, UserName = u.Name, OrderId = o.Id, OrderAmount = o.Amount }) .ToList();

联表查询是进阶必备技能,它能将多次数据库查询合并为一次,极大提升效率。掌握InnerJoinLeftJoinWhere条件和Select投影的写法,是脱离新手村的重要标志。

从环境搭建到完成基础的CRUD,再到事务、同步和性能优化的初步了解,这条路径覆盖了一个ORM项目从零开始所需的核心知识。SqlSugar的文档相当丰富,当你遇到更复杂的需求时,如分库分表、读写分离、过滤器等,其官方文档是最好的下一站。记住,ORM的目的是提升开发效率,而不是完全取代SQL。理解它生成的SQL,并在必要时使用db.Ado.SqlQuery<T>执行原生SQL,才是灵活而强大的做法。

http://www.jsqmd.com/news/1353998/

相关文章:

  • 多机联动机器人上下料:技术可行,但不要盲目追求一台机器人带 N 台机床 - 芈只AI研究院
  • 龙岩全屋漏水别瞎修!9大渗水场景一次讲透,省心修缮不踩坑 - 宅安选房屋修缮
  • DDrawCompat完整指南:让经典DirectX游戏在Windows 11完美运行
  • AutoRemesher:基于各向同性重划分的自动四边形网格生成解决方案
  • Python+Django构建轻量级电子病历管理系统实践
  • VMware虚拟机安装CentOS 7全流程指南:从环境配置到优化排错
  • TimeUtils记录
  • 2026.8月衡水防水补漏维修,卫生间,阳台,外墙,屋顶,地下室漏水根治测评 - 超人防水
  • 2026南阳厂房防水补漏三品牌公开参数与场景对照:工艺/材料/报价/质保(捷修/宅乐安/居固安) - 家居避坑指南
  • 沉浸式电气火灾安全教育系统解析与应用
  • 机器学习模型创建与调用全流程:从数据准备到生产部署
  • 口碑出圈!靠谱AI获客系统源头厂商怎么选? - 品牌品鉴馆
  • Python多任务并行处理实战:线程、进程与协程性能优化指南
  • hacker-slides开发指南:深入了解项目架构与代码实现
  • AI编程助手如何通过Agent Skills实现工程纪律与代码规范
  • Python+OpenClaw实现财务自动化:Excel处理与Agent自治
  • Steam挂刀行情站终极指南:24小时自动追踪四大平台饰品价格的完整教程
  • Navicat重置脚本终极指南:macOS无限试用方案完整解析
  • 从Function Calling到MCP:AI Agent工具调用的三层境界与生产级实践
  • 免费获取高分辨率转录图谱:ProCapNet本地部署与批量处理教程
  • 2026 年8月 甘肃口碑最好的旅行社 **,纯玩合规稳居西北 - 跟我去旅游
  • 2026.8月邢台防水补漏维修,卫生间,阳台,外墙,屋顶,地下室漏水根治测评 - 超人防水
  • 深入解析Hisat2比对日志:从核心指标到RNA-seq数据质控实战
  • 如何用jCodeMunch-MCP节省95%AI代码检索token成本:智能助手的终极解决方案
  • LaTeX数学公式排版指南:从基础语法到实战避坑
  • 2026 金华靠谱准时模具厂深度测评|永康 30 年老牌一站式塑胶模具定制厂家甄选指南 - 闽地观察实事
  • 2026年8月揭晓!专业苏州轻质砖隔墙口碑榜,哪家能拔头筹? - 产品评测官
  • GitLab项目迁移全攻略:一键工具与API实践
  • SSD数据保持力失效:长期断电后数据为何会丢失?
  • Bad BPF项目深度解析:探索7个恶意eBPF程序的攻击原理与防御策略