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. 官方及社区资源链接
- SQLite 官方网站 : www.sqlite.org/
- 微软 Microsoft.Data.Sqlite 官方文档 : docs.microsoft.com/en-us/dotne...
- Entity Framework Core - SQLite 提供程序 : docs.microsoft.com/en-us/ef/co...
- Stack Overflow (SQLite 标签) : stackoverflow.com/questions/t...
- SQLite Tutorial 网站 : www.sqlitetutorial.net/