.NET SQLite


1. 项目集成与配置

要在 .NET 项目中通过代码操作 SQLite,核心是安装 NuGet 包。

  • 核心驱动: Microsoft.Data.Sqlite。这是所有操作的基础。
  • ORM 支持: 若使用 Entity Framework Core,则安装 Microsoft.EntityFrameworkCore.Sqlite 即可。
  • 安装方式 :
    • Visual Studio: 右键项目 -> "管理 NuGet 程序包" -> 搜索并安装。
    • .NET CLI: 运行 dotnet add package Microsoft.Data.Sqlite。

2. 基础操作:数据库与表

这是所有数据操作的起点。

2.1 数据库的创建与删除

  • 数据库创建: SQLite 通过代码隐式创建数据库。当您尝试连接一个不存在的文件时,该文件(即数据库)会被自动创建。
C# 复制代码
  using Microsoft.Data.Sqlite;  
  // 如果文件不存在,Open() 方法会自动创建它  
  using (var connection = new SqliteConnection("Data Source=MyDatabase.db"))  
  {  
      connection.Open();  
  }
  • 数据库删除: 删除数据库即删除其对应的文件,使用标准的 .NET 文件操作即可。
C# 复制代码
 using System.IO;  
  using Microsoft.Data.Sqlite;

  string dbFile = "MyDatabase.db";  
  // 确保没有活动的连接,否则文件会被占用  
  SqliteConnection.ClearAllPools();   
  if (File.Exists(dbFile))  
  {  
      File.Delete(dbFile);  
  }

2.2 表的创建与字段类型说明

通过执行 CREATE TABLE 语句来创建表。

C# 复制代码
using (var connection = new SqliteConnection("Data Source=Company.db"))  
{  
    connection.Open();  
    using (var cmd = connection.CreateCommand())  
    {  
        cmd.CommandText = @"  
            CREATE TABLE Employees (  
                EmployeeID   INTEGER PRIMARY KEY AUTOINCREMENT, -- 整数,主键并自增  
                FirstName    TEXT NOT NULL,                     -- 文本  
                Salary       REAL,                              -- 浮点数  
                HireDate     TEXT,                              -- 日期(存为文本)  
                ProfileImage BLOB,                              -- 二进制数据  
                IsActive     INTEGER DEFAULT 1                  -- 布尔值 (0/1)  
            );  
        ";  
        cmd.ExecuteNonQuery();  
    }  
}

2.3 C# 与 SQLite 数据类型映射

您会看到一个 SQLite 类型对应多个 C# 类型,其根本原因在于两者设计哲学的不同。SQLite 使用的是一种更灵活的"类型亲和性"系统,列会倾向于存储某种类型的数据,但并不严格强制。而 C# 是强类型的,每个变量都必须有一个明确的、不可改变的类型。

因此,当 C# 的 Microsoft.Data.Sqlite 驱动从 SQLite 读取数据时,它提供了多种"转换"的可能性。但是,作为开发者,选择最恰当、最安全的数据类型来接收,是保证程序健壮性的关键。 下表将为您提供详细的映射关系和选型建议

SQLite 类型 存储内容 推荐的 .NET/C# 类型 备注
INTEGER 64位有符号整数 long, int, bool 读首选 long: SQLite 的 INTEGER 存储空间是64位的,与 C# 的 long 完全对应。使用 reader.GetInt64() 是最安全、无损的选择。 谨慎使用 int: 只有当您 100% 确定数据库中的值不会超过 int 的最大值 (约21亿) 时,才应使用 reader.GetInt32()。否则,读取一个大数值会抛出 OverflowException。 用于布尔值: 这是一个常见模式 (0=false, 1=true)。可以直接使用 reader.GetBoolean(),它会自动处理转换。
REAL 64位浮点数 double 首选 double: REAL 与 C# 的 double 都是64位浮点数,这是最直接、无损的映射。请使用 reader.GetDouble()。 谨慎使用 float: float 是32位浮点数。使用 reader.GetFloat() 读取 REAL 值可能会导致精度丢失。仅在精度要求不高时使用。 避免直接映射到 decimal: decimal 是高精度的十进制类型,主要用于金融计算。SqliteDataReader 没有 GetDecimal() 方法。如果您需要 decimal 的精度,最佳实践是在 SQLite 中将数据存为 TEXT (如 "199.99") 或缩放后的 INTEGER (如 19999),在 C# 中读取后再解析为 decimal。
TEXT 文本字符串 string, DateTime, Guid 首选 string: 使用 reader.GetString() 是最标准的操作。 用于日期/时间: 推荐在 SQLite 中以 YYYY-MM-DD HH:MM:SS (ISO 8601) 格式存储。在 C# 中先用 GetString() 读取,再用 DateTime.Parse() 或 DateTime.ParseExact() 进行转换,这样可以避免时区和格式问题。 用于 Guid: 同样,先读取为字符串,再用 Guid.Parse() 转换。
BLOB 二进制数据 byte[] 这是唯一的映射方式,用于存储图片、文件等二进制内容。使用 reader.GetBytes() 读取。
NULL 空值 null (关键避坑点) NULL 不是一个类型,而是一个值。在调用任何 Get...() 方法(如 GetString, GetInt32)之前,必须先使用 reader.IsDBNull(index) 进行检查。直接对 NULL 值调用这些方法会抛出 InvalidCastException。

3. 核心数据操作 (CRUD)

以下是针对 Employees 表的完整、独立的增删改查操作示例。

3.1 增 (Create - INSERT)

C# 复制代码
using (var connection = new SqliteConnection("Data Source=Company.db"))  
{  
    connection.Open();  
    using (var cmd = connection.CreateCommand())  
    {  
        cmd.CommandText = "INSERT INTO Employees (FirstName, Salary) VALUES (@FirstName, @Salary)";  
        cmd.Parameters.AddWithValue("@FirstName", "张三");  
        cmd.Parameters.AddWithValue("@Salary", 8000.50);  
          
        int rowsInserted = cmd.ExecuteNonQuery(); // 返回受影响的行数 (通常为1)  
        Console.WriteLine($"成功插入了 {rowsInserted} 行数据。");  
    }  
}

3.2 查 (Read - SELECT)

C# 复制代码
using (var connection = new SqliteConnection("Data Source=Company.db"))  
{  
    connection.Open();  
    using (var cmd = connection.CreateCommand())  
    {  
        cmd.CommandText = "SELECT EmployeeID, FirstName, Salary FROM Employees";  
        using (var reader = cmd.ExecuteReader())  
        {  
            while (reader.Read())  
            {  
                var id = reader.GetInt32(0);  
                var name = reader.GetString(1);  
                var salary = reader.GetDouble(2);  
                Console.WriteLine($"ID: {id}, 姓名: {name}, 薪水: {salary}");  
            }  
        }  
    }  
}

3.3 改 (Update - UPDATE)

C# 复制代码
using (var connection = new SqliteConnection("Data Source=Company.db"))  
{  
    connection.Open();  
    using (var cmd = connection.CreateCommand())  
    {  
        cmd.CommandText = "UPDATE Employees SET Salary = @NewSalary WHERE FirstName = @Name";  
        cmd.Parameters.AddWithValue("@NewSalary", 8500.00);  
        cmd.Parameters.AddWithValue("@Name", "张三");

        int rowsUpdated = cmd.ExecuteNonQuery(); // 返回被更新的行数  
        Console.WriteLine($"成功更新了 {rowsUpdated} 行数据。");  
    }  
}

3.4 删 (Delete - DELETE)

C# 复制代码
using (var connection = new SqliteConnection("Data Source=Company.db"))  
{  
    connection.Open();  
    using (var cmd = connection.CreateCommand())  
    {  
        cmd.CommandText = "DELETE FROM Employees WHERE FirstName = @Name";  
        cmd.Parameters.AddWithValue("@Name", "张三");

        int rowsDeleted = cmd.ExecuteNonQuery(); // 返回被删除的行数  
        Console.WriteLine($"成功删除了 {rowsDeleted} 行数据。");  
    }  
}

4. 进阶操作

以下示例将使用更复杂的"学生-课程"模型来演示。

4.1 多表关联查询 (JOIN)

  • 核心概念: 使用 JOIN ... ON ... 语法,根据关联字段将多张表的数据组合在一起。

  • 代码示例:

C# 复制代码
 // 此处省略了建表和插入初始数据的代码,假设已存在 Students, Courses, Enrollments 表  
  using (var connection = new SqliteConnection("Data Source=University.db"))  
  {  
      connection.Open();  
      Console.WriteLine("--- 查询所有选了课的学生及其课程 ---");  
      using (var cmd = connection.CreateCommand())  
      {  
          cmd.CommandText = @"  
              SELECT s.Name, c.CourseName  
              FROM Students s  
              JOIN Enrollments e ON s.StudentID = e.StudentID  
              JOIN Courses c ON e.CourseID = c.CourseID;";  
          using (var reader = cmd.ExecuteReader())  
          {  
              while (reader.Read())  
              {  
                  Console.WriteLine($"学生: {reader.GetString(0)}, 所选课程: {reader.GetString(1)}");  
              }  
          }  
      }  
  }
  • 易错点: ON 条件写错;列名不明确时忘记使用表别名前缀。

4.2 结果处理:去重与排序 (DISTINCT & ORDER BY)

  • 核心概念: DISTINCT 移除结果集中完全重复的行;ORDER BY 对结果集排序。

  • 代码示例:

C# 复制代码
 using (var connection = new SqliteConnection("Data Source=University.db"))  
  {  
      connection.Open();  
      Console.WriteLine("\\n--- 查询所有被选过的课程名称(去重并降序排列)---");  
      using (var cmd = connection.CreateCommand())  
      {  
          cmd.CommandText = @"  
              SELECT DISTINCT c.CourseName  
              FROM Courses c  
              JOIN Enrollments e ON c.CourseID = e.CourseID  
              ORDER BY c.CourseName DESC;";  
          using (var reader = cmd.ExecuteReader())  
          {  
              while (reader.Read()) { /\* ... \*/ }  
          }  
      }  
  }
  • 易错点: ORDER BY 必须位于 WHERE/JOIN 之后;多列排序时忘记用逗号分隔。

4.3 事务控制 (Transaction)

  • 核心概念: 将多个操作打包,保证它们"要么全部成功,要么全部失败"。

  • 代码示例:

C# 复制代码
 using (var connection = new SqliteConnection("Data Source=University.db"))  
  {  
      connection.Open();  
      using (var transaction = connection.BeginTransaction())  
      {  
          try  
          {  
              // 操作1  
              using (var cmd1 = connection.CreateCommand())  
              {  
                  cmd1.Transaction = transaction; // 关键:关联事务  
                  cmd1.CommandText = "INSERT INTO Enrollments (StudentID, CourseID) VALUES (3, 2);";  
                  cmd1.ExecuteNonQuery();  
              }

              // 操作2  
              using (var cmd2 = connection.CreateCommand())  
              {  
                  cmd2.Transaction = transaction; // 关键:关联事务  
                  cmd2.CommandText = "DELETE FROM Enrollments WHERE StudentID = 2 AND CourseID = 1;";  
                  cmd2.ExecuteNonQuery();  
              }

              transaction.Commit(); // 全部成功,提交  
              Console.WriteLine("事务成功提交。");  
          }  
          catch (Exception ex)  
          {  
              transaction.Rollback(); // 发生任何错误,回滚  
              Console.WriteLine($"事务回滚: {ex.Message}");  
          }  
      }  
  }
  • 易错点 : 必须为事务中的每个命令设置其 .Transaction 属性,否则该命令无法回滚。

5. 语法对比:SQLite vs. Oracle (含示例)

功能分类 SQLite 示例 Oracle 示例
判空处理 SELECT IFNULL(Major, 'Undeclared') FROM Students; SELECT NVL(Major, 'Undeclared') FROM Students;
获取当前日期 SELECT DATE('now'); SELECT TRUNC(SYSDATE) FROM DUAL;
日期计算 SELECT DATE('now', '+1 month', '-5 day'); SELECT ADD_MONTHS(SYSDATE, 1) - 5 FROM DUAL;
日期格式化 SELECT STRFTIME('%Y年%m月%d日', 'now'); SELECT TO_CHAR(SYSDATE, 'YYYY"年"MM"月"DD"日"') FROM DUAL;
字符串聚合 SELECT GROUP_CONCAT(Name, ', ') FROM Students; SELECT LISTAGG(Name, ', ') WITHIN GROUP (ORDER BY Name) FROM Students;

6. 常见编程异常与避坑指南

异常信息 发生原因 解决方案 / 规避策略
SQLite Error 1: 'no such table: ...' 表名错误,或连接到了错误的数据库文件。 检查拼写;调试时使用绝对路径;确保建表逻辑先于查询逻辑执行。
SQLite Error 5: 'database is locked' (最常见) DataReader 未关闭;其他进程(如DB Browser)锁定了文件。 将 DataReader 和 Connection 放入 using 块中;保持事务简短;检查并关闭其他锁定文件的程序。
SQLite Error 19: 'UNIQUE constraint failed...' 插入了违反 UNIQUE 或 PRIMARY KEY 约束的重复数据。 在 INSERT 前先 SELECT 检查;使用 try-catch 捕获并处理冲突;或使用 INSERT OR IGNORE 等SQLite特定语法。
System.InvalidCastException: 'Unable to cast object of type 'DBNull' to type ...' (新手高频错误) 调用 reader.GetString() 等强类型方法时,数据库中对应的值恰好是 NULL。 最佳实践 : 在读取任何可能为 NULL 的列之前,必须先使用 reader.IsDBNull(index) 进行判断
'Connection must be open.' 在未打开或已关闭的连接上执行命令。 在 using 块内,确保在执行命令前调用 connection.Open()。
'Cannot access a disposed object...' 在 using 块结束后,仍有代码尝试使用已释放的连接。 确保所有数据库操作都在 using 块内完成。如果方法需返回查询结果,请先用 .ToList() 将其物化为列表再返回。

7. 官方及社区资源链接

相关推荐
小蜗牛编程实录3 小时前
1.多线程核心知识梳理:从进程关系到实践要点
后端
小蜗牛编程实录3 小时前
6.MySQL 主从复制深度解析:原理、问题与优化方案
后端
xxxcq3 小时前
Go微服务网关开发(3):负载均衡功能的实现
后端·github
uhakadotcom3 小时前
常识:python之中的伪随机数安全风险
后端·面试·github
Goboy4 小时前
【Python修仙笔记.3】Python函数作为秘技 - 封装你的仙法
后端·python
Goboy4 小时前
【Python修仙笔记.4】数据结构法宝 - 存储你的仙器
后端·python
间彧4 小时前
Spring Boot自动配置与"约定大于配置"机制详解
后端
IT_陈寒4 小时前
JavaScript引擎优化:5个90%开发者都不知道的V8隐藏性能技巧
前端·人工智能·后端
JaguarJack5 小时前
PHP "真异步" TrueAsync SAPI 与 NGINX Unit 集成
后端·php