1. 项目概述为什么选择SqlSugar与SQLite这对黄金搭档如果你刚开始接触.NET开发或者正在寻找一个轻量、快速上手的数据持久化方案那么“SqlSugar操作SQLite数据库”这个组合绝对值得你花时间研究。我最初接触这个组合是在一个需要快速原型验证的小工具项目里。当时的需求很简单需要一个零配置、单文件、无需安装额外服务的数据库同时操作数据库的代码要足够简洁优雅不能到处都是拼凑的SQL字符串。SQLite和SqlSugar的相遇完美地解决了这两个痛点。SQLite你可能早就听说过它是一个进程内的、无服务器的、零配置的、事务性的SQL数据库引擎。说人话就是它就是一个.db或.sqlite文件你的程序直接读写这个文件不需要像MySQL或SQL Server那样先安装、再启动一个数据库服务。这对于客户端应用、移动应用、小型网站或者测试环境来说简直是神器。部署简单拷贝文件就行开发也简单用个可视化工具比如DB Browser for SQLite就能直接查看和修改数据。而SqlSugar是一个在国内开发者中非常流行的.NET ORM框架。ORMObject-Relational Mapping对象关系映射它的目标就是让你能用操作C#对象类的方式去操作数据库表省去手写大量枯燥且易错的ADO.NET代码。SqlSugar以其高性能、语法糖丰富顾名思义、以及对中国开发者友好的中文文档和社区支持而著称。它的链式查询语法写起来非常流畅学习曲线相对平缓。把这两者结合起来你得到的就是一个“开箱即用”的快速开发数据层方案。你不需要操心数据库安装只需要一个NuGet包和一个数据库文件你也不需要深入学习复杂的SQL和ADO.NET就能完成绝大多数增删改查CRUD操作。这对于入门者构建第一个有数据存储功能的小项目或者老手快速搭建演示、测试项目效率提升是立竿见影的。接下来我会带你从零开始一步步搭建环境完成基础的CRUD并分享一些我实际使用中总结出来的经验和避坑点。2. 环境准备与项目搭建2.1 创建项目与安装NuGet包首先我们得有一个.NET项目。这里以最常见的控制台应用为例使用.NET 6或更高版本它们都是长期支持版本且项目文件简洁。你可以通过Visual Studio的创建向导或者直接用命令行dotnet new console -n SqlSugarWithSQLiteDemo cd SqlSugarWithSQLiteDemo项目创建好后我们需要引入两个核心的NuGet包。你可以通过Visual Studio的NuGet包管理器图形界面搜索安装或者使用包管理器控制台当然最“极客”的方式还是命令行dotnet add package sqlite-net-pcl dotnet add package sqlsugarcore这里解释一下为什么是两个包sqlite-net-pcl 这是SQLite的.NET实现提供了与SQLite数据库引擎交互的基础能力。SqlSugar底层需要依赖它来连接和操作SQLite文件。sqlsugarcore 这就是SqlSugar ORM框架本身的核心库。注意如果你用的是.NET Framework的老项目可能需要安装SqlSugar包但对于.NET Core/.NET 5的项目SqlSugarCore是更好的选择。安装完成后你的项目文件.csproj里应该能看到对应的包引用。这一步是基石务必确保安装成功没有版本冲突。2.2 初始化数据库与SqlSugar配置安装好包之后我们就要开始写代码了。首先在Program.cs如果是.NET 6的单文件模板或者你定义的启动类里我们需要配置SqlSugar来连接我们的SQLite数据库。假设我们的数据库文件叫demo.db放在程序运行目录下。我们来创建一个简单的配置类或方法using SqlSugar; using System; namespace SqlSugarWithSQLiteDemo { public class Program { // 声明一个静态的SqlSugarClient实例方便全局使用对于简单Demo或小应用可以这样生产环境建议依赖注入 public static SqlSugarClient Db; static void Main(string[] args) { // 1. 配置数据库连接 Db new SqlSugarClient(new ConnectionConfig() { ConnectionString Data Source./demo.db, // 连接字符串指向当前目录下的demo.db文件 DbType DbType.Sqlite, // 数据库类型 IsAutoCloseConnection true, // 是否自动关闭连接建议设为true InitKeyType InitKeyType.Attribute // 初始化主键和自增列的方式通过特性Attribute }); // 2. 输出SQL日志开发调试非常有用 Db.Aop.OnLogExecuting (sql, pars) { Console.WriteLine($【SQL语句】: {sql}); // 如果需要打印参数可以这样 // if (pars ! null pars.Length 0) // { // Console.WriteLine($【参数】: {string.Join(, , pars.Select(p ${p.ParameterName}:{p.Value}))}); // } }; Console.WriteLine(数据库连接配置完成); // 后续的演示代码将在这里调用Db对象 // CreateTableIfNotExists(); // InsertData(); // QueryData(); // UpdateData(); // DeleteData(); } } }关键配置解析ConnectionString:Data Source./demo.db。这是SQLite最基础的连接字符串格式./表示当前程序运行目录。你也可以用绝对路径如D:\mydata\demo.db。DbType: 必须指定为DbType.Sqlite告诉SqlSugar我们连接的是哪种数据库。IsAutoCloseConnection: 设置为true是个好习惯。这意味着每次执行完数据库操作SqlSugar会自动帮你关闭连接避免连接泄露。对于SQLite这种文件数据库虽然连接泄露的后果不像服务器数据库那么严重但保持良好的习惯总是对的。InitKeyType: 设置为InitKeyType.Attribute。这表示我们将通过C#属性上的特性如[SugarColumn(IsPrimaryKey true)]来标识主键和自增列。这是SqlSugar推荐的方式清晰且与实体类强关联。Aop.OnLogExecuting: 这是一个事件钩子当SqlSugar执行SQL前会触发。我们把执行的SQL语句打印到控制台。这是开发阶段最重要的调试工具你能看到SqlSugar帮你生成了什么样的SQL对于排查问题、理解ORM行为有巨大帮助。注意在生产环境中日志输出需要更谨慎避免将敏感数据或过多的日志输出到控制台应集成到如Serilog、NLog等日志框架中。3. 定义实体类与表结构映射ORM的核心思想是“对象-关系映射”。所以我们需要先定义C#的实体类对象来对应数据库中的表。SqlSugar提供了丰富的特性Attribute来定义这种映射关系。假设我们要创建一个Student学生表。我们先在项目中创建一个Models文件夹然后添加Student.cs类using SqlSugar; namespace SqlSugarWithSQLiteDemo.Models { [SugarTable(Student)] // 指定该实体类映射到数据库中的表名如果不加默认用类名 public class Student { [SugarColumn(IsPrimaryKey true, IsIdentity true)] // 标识为主键且自增 public int Id { get; set; } [SugarColumn(Length 50, IsNullable false)] // 长度50不可为空NOT NULL public string Name { get; set; } public int Age { get; set; } [SugarColumn(Length 100, IsNullable true)] // 长度100可以为空 public string? Email { get; set; } // C# 8.0 的可空引用类型与IsNullabletrue对应 [SugarColumn(ColumnDataType datetime, IsNullable true)] public DateTime? EnrollmentDate { get; set; } // 可为空的日期时间 // 这是一个不映射到数据库的普通属性 [SugarColumn(IsIgnore true)] public string DisplayInfo ${Name} (Age: {Age}); } }特性详解与避坑指南[SugarTable] 类级别的特性。用于指定实体对应的数据库表名。如果省略SqlSugar默认使用类名作为表名区分大小写取决于数据库SQLite默认不区分。建议显式指定避免因类名更改或数据库命名规范不同导致问题。[SugarColumn] 属性级别的特性功能强大。IsPrimaryKey true: 声明该属性是表的主键。IsIdentity true: 声明该列是自增列通常与主键Id搭配使用。对于SQLite自增主键通常是INTEGER类型。Length: 设置字符串字段的最大长度。对于string类型强烈建议设置Length否则SqlSugar可能会使用默认长度如nvarchar(max)在SQLite中虽然可能没问题但不利于数据规范化和迁移到其他数据库。IsNullable: 指示该列是否允许为NULL。这里的设置需要与C#属性的可空性匹配。例如string? Email对应IsNullable truestring Name对应IsNullable false。如果不匹配在生成表或插入数据时可能会出错。ColumnDataType: 直接指定数据库中的列类型如datetime,decimal(10,2)。当SqlSugar默认推断的类型不符合你要求时使用。例如SQLite的DateTime映射有时需要明确。IsIgnore true: 该属性将不会映射到数据库表中。常用于计算属性、临时字段或敏感信息。命名规范 SqlSugar默认使用“原样映射”即属性名Name对应列名Name。如果你想使用不同的命名约定如驼峰转下划线可以在ConnectionConfig中配置MoreSettings但这属于进阶内容入门期保持默认即可。实操心得在项目初期花点时间设计好实体类结构合理使用特性能为后续开发省去大量麻烦。特别是主键、自增、可空性和长度的设定是数据完整性的第一道防线。4. 基础CRUD操作详解环境、配置、实体类都准备好了现在让我们进入最核心的部分——增删改查。我会用最直观的链式调用语法来演示。4.1 创建表Code First在插入数据前我们需要确保表存在。SqlSugar的Code First功能可以帮你根据实体类自动创建或更新表结构。在Main方法中调用以下方法static void CreateTableIfNotExists() { // Db.CodeFirst 是Code First操作的入口 // .StringDefaultLength(50) 设置string类型属性的默认长度避免每个属性都写Length // .InitTables(typeof(Student)) 初始化指定实体类对应的表 // 如果表不存在则创建存在则检查列缺少的列会自动添加但不会删除列或修改列类型这是安全策略 Db.CodeFirst.SetStringDefaultLength(50).InitTables(typeof(Student)); Console.WriteLine(Student表检查/创建完成。); }执行这个方法后去你的项目目录下看看demo.db文件应该已经被创建了。你可以用DB Browser for SQLite打开它会发现里面多了一个Student表字段和我们在Student类中定义的一模一样。注意InitTables是“初始化”表它只负责创建不存在的表和添加不存在的列。它不会删除表中已有的列也不会修改已有列的数据类型。这是一种安全的同步策略。如果你需要修改列类型或删除列需要手动执行SQLDb.Ado.ExecuteCommand或使用更高级的迁移工具。对于入门项目通常重建数据库文件删除旧的.db文件再运行InitTables更简单。4.2 插入数据Create插入单条数据非常简单static void InsertSingleStudent() { var newStudent new Student { Name 张三, Age 20, Email zhangsanexample.com, EnrollmentDate DateTime.Now }; // 插入并返回受影响的行数通常是1 var count Db.Insertable(newStudent).ExecuteCommand(); Console.WriteLine($插入了 {count} 条数据。新学生的ID是{newStudent.Id}); // 插入并返回自增的主键值推荐更直观 var id Db.Insertable(newStudent).ExecuteReturnIdentity(); Console.WriteLine($插入了数据新学生的ID是{id}。实体对象Id属性也被更新了{newStudent.Id}); }关键点Insertable(T) 创建一个插入操作构建器。ExecuteCommand() 执行命令返回受影响行数。ExecuteReturnIdentity() 执行命令并返回插入行的自增主键值。注意执行此方法后传入的实体对象newStudent的Id属性也会被自动赋值为这个新ID非常方便。批量插入可以使用Insertable(ListT)然后调用ExecuteCommandAsync或ExecuteReturnIdentityAsync异步版本效率更高。4.3 查询数据Read查询是ORM最常用的功能SqlSugar的查询语法非常强大且易读。1查询所有记录static void QueryAllStudents() { var list Db.QueryableStudent().ToList(); Console.WriteLine($共有 {list.Count} 个学生); foreach (var stu in list) { Console.WriteLine($ ID:{stu.Id}, 姓名:{stu.Name}, 年龄:{stu.Age}, 邮箱:{stu.Email}); } }QueryableT()是查询的起点.ToList()将结果转换为ListT。2带条件的查询链式调用static void QueryWithCondition() { // 查询年龄大于18岁且姓名包含“张”的学生按年龄降序排列 var list Db.QueryableStudent() .Where(stu stu.Age 18) // Lambda表达式条件 .Where(stu stu.Name.Contains(张)) // 可以链式多个Where效果是AND .OrderBy(stu stu.Age, OrderByType.Desc) // 排序 .ToList(); // 或者使用更简洁的语法多个条件在一个Where里 // .Where(stu stu.Age 18 stu.Name.Contains(张)) Console.WriteLine($符合条件的學生有 {list.Count} 个。); }3查询单条记录static void QuerySingleStudent() { // 根据主键查询最常用 var student Db.QueryableStudent().InSingle(1); // 查询Id1的学生 if (student ! null) { Console.WriteLine($找到学生{student.Name}); } // 根据其他条件查询单条如果有多条会抛出异常 var student2 Db.QueryableStudent().Where(stu stu.Name 李四).First(); // 使用FirstOrDefault更安全查不到返回null var student3 Db.QueryableStudent().Where(stu stu.Name 王五).FirstOrDefault(); }4分页查询分页是Web开发中的必备技能。static void QueryWithPaging() { int pageIndex 1; // 第1页 int pageSize 5; // 每页5条 int totalCount 0; // 用于接收总记录数 var pageList Db.QueryableStudent() .OrderBy(stu stu.Id) // 分页必须排序 .ToPageList(pageIndex, pageSize, ref totalCount); // 关键方法 Console.WriteLine($第{pageIndex}页共{Math.Ceiling(totalCount * 1.0 / pageSize)}页本页{pageList.Count}条总计{totalCount}条。); foreach (var stu in pageList) { Console.WriteLine($ ID:{stu.Id}, 姓名:{stu.Name}); } }ToPageList方法非常方便一次性获取当前页数据和总记录数。4.4 更新数据Update更新操作需要指定要更新的实体和更新条件。static void UpdateStudent() { // 方式1先查询修改对象然后更新全字段更新 var stuToUpdate Db.QueryableStudent().InSingle(1); if (stuToUpdate ! null) { stuToUpdate.Age 21; stuToUpdate.Email updatedexample.com; var count Db.Updateable(stuToUpdate).ExecuteCommand(); Console.WriteLine($更新了 {count} 条数据。); } // 方式2只更新指定字段更高效推荐 var count2 Db.UpdateableStudent() .SetColumns(stu new Student { Age 22, Email partialexample.com }) // 只更新Age和Email .Where(stu stu.Id 1) .ExecuteCommand(); Console.WriteLine($部分更新了 {count2} 条数据。); // 方式3基于原值的更新如年龄1 var count3 Db.UpdateableStudent() .SetColumns(stu stu.Age stu.Age 1) // 年龄自增1 .Where(stu stu.Id 2) .ExecuteCommand(); Console.WriteLine($自增更新了 {count3} 条数据。); }实操心得在大多数业务场景中方式2部分更新是最佳实践。它只生成更新特定字段的SQL如UPDATE Student SET Age22, Emailpartialexample.com WHERE Id1避免了全字段更新可能带来的并发问题如其他人修改了Name字段却被你无意中覆盖回旧值并且性能更好。4.5 删除数据Delete删除操作相对简单但需谨慎。static void DeleteStudent() { // 根据主键删除 var count1 Db.DeleteableStudent().In(1).ExecuteCommand(); // 删除Id1的记录 Console.WriteLine($根据主键删除了 {count1} 条数据。); // 根据条件删除 var count2 Db.DeleteableStudent() .Where(stu stu.Age 18) // 删除年龄小于18岁的学生 .ExecuteCommand(); Console.WriteLine($根据条件删除了 {count2} 条数据。); // 删除所有数据危险慎用 // var count3 Db.DeleteableStudent().ExecuteCommand(); }重要警告DeleteableT().ExecuteCommand()不带任何条件时会删除整张表的所有数据在生产环境中执行删除操作前务必再三确认Where条件最好先Select一下看看会影响到哪些数据。对于重要数据建议采用“软删除”即增加一个IsDeleted标志位用Update来标记删除而非物理删除。5. 进阶技巧与常见问题排查掌握了基础的CRUD你已经可以应对很多场景了。但在实际项目中总会遇到一些更复杂的需求或奇怪的问题。这里分享几个我踩过坑后总结的进阶技巧和排查方法。5.1 事务处理数据库事务用于确保一系列操作要么全部成功要么全部失败。SqlSugar提供了非常简洁的事务支持。static void TransactionDemo() { try { Db.Ado.BeginTran(); // 开始事务 // 操作1插入学生A var stuA new Student { Name 事务学生A, Age 25 }; Db.Insertable(stuA).ExecuteReturnIdentity(); // 操作2插入学生B var stuB new Student { Name 事务学生B, Age 26 }; Db.Insertable(stuB).ExecuteReturnIdentity(); // 模拟一个可能失败的操作 // int a 0; // int b 1 / a; // 这里会抛出除以零异常触发回滚 Db.Ado.CommitTran(); // 提交事务 Console.WriteLine(事务提交成功两条数据均已插入。); } catch (Exception ex) { Db.Ado.RollbackTran(); // 回滚事务 Console.WriteLine($事务执行失败已回滚。错误{ex.Message}); } }关键点务必使用try-catch块包裹事务操作在catch中执行回滚。BeginTran,CommitTran,RollbackTran必须配对使用。也可以使用Db.UseTran(() { ... })的语法糖它自动处理提交和回滚代码更简洁。5.2 使用AOP日志排查问题我们在配置阶段已经开启了SQL日志。这是排查问题最强大的武器。当你发现查询结果不对、更新失败时第一件事就是去看控制台输出的SQL语句。常见问题1查询条件没生效看看生成的SQL的WHERE子句对不对参数值是否正确传递了。常见问题2插入失败主键冲突检查是不是手动设置了自增主键Id的值而该值已存在。常见问题3更新了不该更新的字段检查SetColumns语句看它生成的SET部分是否只包含了你想要的字段。养成看SQL日志的习惯你能更深入地理解SqlSugar在背后做了什么也能更快地定位问题是出在ORM配置上还是出在SQL逻辑本身。5.3 实体类与数据库表不同步怎么办这是Code First开发中常见的问题。比如你给Student类新增了一个Address属性但数据库表里没有这个字段。安全模式默认使用Db.CodeFirst.InitTables它会添加新列但不会删除或修改已有列。对于新增属性这通常没问题。强制同步危险SqlSugar提供了Db.CodeFirst.SetStringDefaultLength(50).InitTablesT(true)的重载传入true参数可以尝试更激进的同步如修改列类型但行为可能因数据库而异且有丢失数据的风险不建议在生产数据库上直接使用。推荐做法对于已有数据的表结构变更最稳妥的方式是编写SQL迁移脚本如ALTER TABLE Student ADD COLUMN Address TEXT;。使用Db.Ado.ExecuteCommand来执行这个脚本。或者使用专业的数据库迁移工具如FluentMigrator并与SqlSugar结合。5.4 性能优化小贴士批量操作插入、更新、删除大量数据时务必使用批量方法如Insertable(ListT)而不是在循环中执行单条操作。性能差异可能是几十倍甚至上百倍。只查询需要的字段如果实体类有很多字段如大文本、二进制字段但本次查询只需要其中几个可以使用.Select(stu new { stu.Id, stu.Name })来投影到匿名对象减少数据传输量。合理使用索引虽然SqlSugar不直接创建索引但你应该根据查询条件特别是Where和OrderBy中的字段在数据库表中创建合适的索引。这能极大提升查询速度。可以通过Db.Ado.ExecuteCommand(CREATE INDEX ...)来执行创建索引的SQL。关闭不必要的日志生产环境中记得关闭或调整Aop.OnLogExecuting的日志级别避免输出海量SQL日志影响性能。5.5 常见错误速查表错误现象可能原因解决方案SqlSugar.SqlSugarException: Connection open error .1. 数据库文件路径错误或不可写。2. 文件被其他进程独占锁定如另一个程序打开着.db文件。1. 检查ConnectionString中的路径确保程序有读写权限。2. 关闭所有可能访问该文件的程序如DB Browser。Microsoft.Data.Sqlite.SqliteException: SQLite Error 1: no such table: Student.表不存在。确保已成功执行Db.CodeFirst.InitTables创建表。检查实体类是否添加了[SugarTable]特性或类名是否正确。SqlSugar.SqlSugarException: The entity does not have a primary key.执行InSingle、按主键更新/删除等操作时实体类没有用[SugarColumn(IsPrimaryKeytrue)]标记主键。在实体类中正确定义主键属性。插入后newStudent.Id始终为01. 主键未设置为自增IsIdentitytrue。2. 使用了ExecuteCommand()而不是ExecuteReturnIdentity()。1. 检查主键属性的[SugarColumn]特性。2. 插入后需要获取自增ID时使用ExecuteReturnIdentity()。查询结果与预期不符1. 查询条件Lambda写错了。2. 大小写敏感问题SQLite默认不区分大小写但某些操作可能区分。1. 查看AOP输出的SQL日志核对生成的WHERE条件。2. 在查询中使用SqlFunc.ToLower()或SqlFunc.ToUpper()进行规范化比较。更新了所有记录Updateable操作忘记了加.Where()条件。立即检查代码更新和删除操作前必须明确指定Where条件。这是最容易出事故的地方。掌握了这些基础操作、进阶技巧和排错方法你已经可以自信地使用SqlSugar和SQLite来为你的.NET项目构建数据访问层了。这个组合在轻量级应用、原型开发、单元测试等领域有着极高的效率和便利性。记住多动手实践多查看生成的SQL日志是掌握任何ORM框架的最佳途径。