
1. 为什么选择SqlSugar SQLite作为你的第一个ORM项目如果你刚开始接触后端开发或者想给自己的桌面应用、移动端App加一个轻量级的本地数据存储那么“数据库”这个词可能会让你感到既熟悉又陌生。熟悉是因为它无处不在陌生是因为从写SQL语句到真正在代码里把数据存进去、查出来中间好像隔着一道鸿沟。直接拼装SQL字符串太容易出错而且代码又臭又长。这时候ORM对象关系映射工具就像一位翻译官帮你把面向对象的代码C#里的类和关系型数据库的表自动对应起来让你能用操作“对象”的方式去操作“数据”。在.NET生态里SqlSugar是近年来非常热门的一个国产ORM框架。它轻量、高性能、语法糖丰富这也是它名字的由来对开发者非常友好。而SQLite则是一个无需安装、配置、零管理的嵌入式数据库一个.db文件就是整个数据库完美契合开发测试、单机应用、移动端等场景。把这两者结合起来你几乎可以零成本地搭建一个可用的数据访问层快速验证你的业务逻辑。这比一上来就折腾SQL Server、MySQL的安装和远程连接要友好得多。我见过很多新手项目死在环境配置上而SqlSugarSQLite这个组合能让你跳过那些坑直接专注于业务代码本身体验从零到一建立起数据持久化能力的完整过程这种正反馈对学习至关重要。2. 项目起手式环境搭建与第一个DbContext理论说再多不如动手。我们首先需要一个.NET项目。这里以主流的.NET 6/8的控制台应用为例当然你也可以用在ASP.NET Core Web API、WPF、MAUI等项目里原理相通。第一步创建项目与安装NuGet包打开你的IDEVisual 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#类型智能推断除非有特殊需求。踩坑点1SQLite的“伪自增”很多人以为在SQLite里把主键设为INTEGER PRIMARY KEY就会自动自增。实际上如果你在插入时显式指定了该列的值即使为0或NULLSQLite会尝试使用你提供的值可能导致主键冲突。而SqlSugar的IsIdentitytrue会在插入时主动忽略这个属性的值确保由数据库分配ID这才是真正的“自增”行为。所以在定义实体时明确标出IsIdentity很重要。踩坑点2字符串长度与索引SQLite对VARCHAR长度限制比较宽松但如果你知道某个字段如用户名、邮箱有最大长度最好用ColumnDataType或Length属性[SugarColumn(Length100)]进行限定。这不仅是数据完整性的要求更重要的是如果你未来要为这个字段创建索引不定长度的字段可能会影响索引效率和查询性能。虽然入门阶段可能用不到但养成好习惯能避免后期重构。4. 从创建表到增删改查完整的CRUD实战DbContext和实体类都准备好了现在让我们把它们用起来。我们在Program.cs或Main方法中编写以下代码。4.1 创建数据库表Code FirstCode 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 zhangsanexample.com, CreatedAt DateTime.Now }; // Insertable 返回一个插入构建器ExecuteReturnIdentity 执行插入并返回自增的主键Id var userId dbContext.Db.Insertable(newUser).ExecuteReturnIdentity(); Console.WriteLine($插入成功用户Id: {userId});批量插入能极大提升性能var userList new ListUser { new User { Name 李四, Age 30, CreatedAt DateTime.Now }, new User { Name 王五, Age 28, Email wangwuexample.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.QueryableUser().ToList(); foreach (var user in allUsers) { Console.WriteLine($ID:{user.Id}, 姓名:{user.Name}, 年龄:{user.Age}); }QueryableT()是查询的起点ToList()执行查询并将结果映射到ListUser。2带条件的查询Where// 查询年龄大于25岁的用户 var adultUsers dbContext.Db.QueryableUser() .Where(u u.Age 25) .ToList(); // 查询邮箱不为空的用户并按年龄倒序排列 var usersWithEmail dbContext.Db.QueryableUser() .Where(u u.Email ! null) .OrderBy(u u.Age, OrderByType.Desc) // OrderByType.Desc 表示降序 .ToList();这里的Where和OrderBy用法和LINQ几乎一样SqlSugar会将其转换为对应的SQLWHERE和ORDER BY子句。打开之前配置的SQL输出你就能看到生成的语句。3分页查询分页是Web应用中最常见的需求。int pageIndex 1; // 第1页 int pageSize 10; // 每页10条 RefAsyncint totalCount 0; // 用于接收总记录数 var pagedList await dbContext.Db.QueryableUser() .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.QueryableUser().Select(u u.Name).ToList(); // 查询用户平均年龄 var avgAge dbContext.Db.QueryableUser().Avg(u u.Age); // 类似的还有 .Sum(), .Max(), .Min(), .Count()4.4 更新数据Update更新操作需要指定条件否则会变成全表更新极其危险SqlSugar默认有保护机制但自己也要小心。// 更新指定Id的用户信息 var updateResult dbContext.Db.UpdateableUser() .SetColumns(u new User() { Name 张老三, Email newemailexample.com }) .Where(u u.Id userId) .ExecuteCommand(); // 另一种更简洁的写法根据实体对象的主键值进行更新 var userToUpdate dbContext.Db.QueryableUser().First(u u.Id userId); if (userToUpdate ! null) { userToUpdate.Age 26; userToUpdate.Email updatedexample.com; dbContext.Db.Updateable(userToUpdate).ExecuteCommand(); }SetColumns方法允许你只更新指定的列这对于只需要修改部分字段的场景非常高效能避免全字段更新带来的并发问题和不必要的网络传输。4.5 删除数据Delete删除操作更要慎之又慎务必确保Where条件准确。// 删除指定Id的用户 var deleteResult dbContext.Db.DeleteableUser() .Where(u u.Id someId) .ExecuteCommand(); // 根据主键值直接删除 dbContext.Db.DeleteableUser().In(1).ExecuteCommand(); // 删除Id为1的记录 dbContext.Db.DeleteableUser().In(new[]{1,2,3}).ExecuteCommand(); // 删除Id为1,2,3的记录In方法对于按主键批量删除非常方便。5. 进阶技巧与实战避坑指南掌握了基本的CRUD你已经可以应付大多数简单场景。但要想写出更健壮、高效的代码还需要了解下面这些进阶知识和常见陷阱。5.1 事务处理保证数据一致性事务用于确保一系列数据库操作要么全部成功要么全部失败。比如转账操作A账户扣钱和B账户加钱必须同时成功。try { dbContext.Db.Ado.BeginTran(); // 开始事务 // 操作1A账户扣款 dbContext.Db.UpdateableAccount().SetColumns(a a.Balance - 100).Where(a a.Id 1).ExecuteCommand(); // 模拟一个可能失败的操作 // int a 0; // int b 1 / a; // 这里会抛出异常触发回滚 // 操作2B账户收款 dbContext.Db.UpdateableAccount().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层面不直接创建索引但你要知道在Where、OrderBy、GroupBy中频繁使用的字段应该在数据库层面建立索引。对于SQLite你可以在实体类特性中标注[SugarColumn(IsIndex true)]然后在InitTables时让SqlSugar帮你创建或者手动执行SQLCREATE INDEX IX_Users_Age ON Users(Age);。警惕N1查询问题这是一个ORM通病。例如你先查询一个用户列表然后遍历每个用户去查询他的订单详情就会产生1次查询用户 N次查询订单的N1次查询。解决方法是使用联表查询或导航属性SqlSugar支持[Navigate]特性。5.4 联表查询入门假设我们还有一个Order表通过UserId关联User表。SqlSugar的联表查询非常强大。// 假设有Order实体包含UserId字段 var list dbContext.Db.QueryableUser() .LeftJoinOrder((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();联表查询是进阶必备技能它能将多次数据库查询合并为一次极大提升效率。掌握InnerJoin、LeftJoin、Where条件和Select投影的写法是脱离新手村的重要标志。从环境搭建到完成基础的CRUD再到事务、同步和性能优化的初步了解这条路径覆盖了一个ORM项目从零开始所需的核心知识。SqlSugar的文档相当丰富当你遇到更复杂的需求时如分库分表、读写分离、过滤器等其官方文档是最好的下一站。记住ORM的目的是提升开发效率而不是完全取代SQL。理解它生成的SQL并在必要时使用db.Ado.SqlQueryT执行原生SQL才是灵活而强大的做法。