SQLite是目前最流行的嵌入式关系型数据库之一,它最大的特点是整个数据库就是磁盘上的一个单独文件,不需要单独安装和启动数据库服务进程。对于桌面工具、小型管理系统、本地缓存这类场景,用C#配合SQLite比部署SQL Server要轻便得多。本文将以System.Data.SQLite这个官方推荐的驱动库为例,完整演示从建库建表到增删改查再到事务处理的全部流程。

一、环境准备与驱动安装
操作SQLite数据库的第一步是引入驱动。在.NET项目中最常用的是System.Data.SQLite,它是一个开源的ADO.NET提供程序,由SQLite官方维护。打开Visual Studio,在NuGet包管理器中搜索System.Data.SQLite并安装即可,或者在程序包管理器控制台直接执行安装命令。
Install-Package System.Data.SQLite
安装完成后,项目中会自动引入System.Data.SQLite命名空间。如果你使用的是.NET Core或.NET 5以上版本,也可以选择Microsoft.Data.Sqlite,它的用法类似但更轻量。本文以System.Data.SQLite为例进行讲解,因为它对传统ADO.NET的兼容性更好,熟悉SqlClient写法的开发者几乎可以无缝切换。
需要特别注意的一点是程序集位数问题。System.Data.SQLite包含非托管代码,在早期版本中需要区分x86和x64。现在的NuGet包会自动处理依赖,但如果编译时报无法加载SQLite.Interop.dll的错误,可以在项目属性中把平台目标改为x86或x64其中之一,不要使用Any CPU。
二、创建数据库与建表
SQLite创建数据库的方式非常直接:只要在连接字符串里写上一个不存在的文件路径,连接打开时就会自动创建这个数据库文件。下面这段代码演示了如何创建一个名为test.db的数据库文件,并在其中创建一张用户表。
using System;
using System.Data.SQLite;
class Program
{
static void Main()
{
// 连接字符串中的Data Source指向文件路径,文件不存在时自动创建
string connStr = "Data Source=test.db;Version=3;";
using (SQLiteConnection conn = new SQLiteConnection(connStr))
{
conn.Open();
string sql = @"CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER,
email TEXT)";
using (SQLiteCommand cmd = new SQLiteCommand(sql, conn))
{
cmd.ExecuteNonQuery();
Console.WriteLine("数据库和表创建成功");
}
}
}
}这段代码有几个值得关注的细节。首先是id INTEGER PRIMARY KEY AUTOINCREMENT,这是SQLite实现自增主键的标准写法,插入数据时不指定id字段即可自动递增。其次是CREATE TABLE IF NOT EXISTS,加上这个判断可以避免重复执行建表语句时报错,这个习惯在程序初始化时特别实用。
另外,SQLite是弱类型数据库,字段声明的TEXT、INTEGER更多是一种类型倾向而不是强制约束,往INTEGER字段里存字符串在默认情况下也不会报错。如果你希望严格校验类型,可以在建表语句末尾加上STRICT关键字(需要SQLite 3.37以上版本),这样类型不匹配就会直接抛出异常,行为更接近传统关系型数据库。
三、增删改查操作详解
有了表之后就可以进行数据操作了。执行增删改用ExecuteNonQuery方法,它返回受影响的行数;执行查询用ExecuteReader遍历结果集,或者用ExecuteScalar获取单个值(比如统计数量)。下面的示例完整演示了插入、查询、更新和删除。
string connStr = "Data Source=test.db;Version=3;";
using (SQLiteConnection conn = new SQLiteConnection(connStr))
{
conn.Open();
// 插入数据,使用参数化查询
string insertSql = "INSERT INTO users (name, age, email) VALUES (@name, @age, @email)";
using (SQLiteCommand cmd = new SQLiteCommand(insertSql, conn))
{
cmd.Parameters.AddWithValue("@name", "张三");
cmd.Parameters.AddWithValue("@age", 28);
cmd.Parameters.AddWithValue("@email", "zhangsan@ipipp.com");
int rows = cmd.ExecuteNonQuery();
Console.WriteLine($"插入成功,受影响行数:{rows}");
}
// 查询数据
string selectSql = "SELECT id, name, age, email FROM users WHERE age > @age";
using (SQLiteCommand cmd = new SQLiteCommand(selectSql, conn))
{
cmd.Parameters.AddWithValue("@age", 20);
using (SQLiteDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine($"ID:{reader.GetInt32(0)}, 姓名:{reader.GetString(1)}, 年龄:{reader.GetInt32(2)}, 邮箱:{reader.GetString(3)}");
}
}
}
// 更新数据
using (SQLiteCommand cmd = new SQLiteCommand("UPDATE users SET age = @age WHERE name = @name", conn))
{
cmd.Parameters.AddWithValue("@age", 29);
cmd.Parameters.AddWithValue("@name", "张三");
cmd.ExecuteNonQuery();
}
// 删除数据
using (SQLiteCommand cmd = new SQLiteCommand("DELETE FROM users WHERE id = @id", conn))
{
cmd.Parameters.AddWithValue("@id", 1);
cmd.ExecuteNonQuery();
}
}这里要重点强调参数化查询的写法。SQLite的参数占位符以@开头,通过Parameters.AddWithValue方法传值。千万不要用字符串拼接的方式把用户输入直接拼进SQL语句,那会直接引入SQL注入漏洞。参数化写法不仅安全,还能让驱动正确处理字符串中的单引号、反斜杠等特殊字符,避免各种奇怪的转义问题。
读取数据时要注意空值处理。如果某个字段可能为NULL,用reader.GetInt32(2)直接取值会抛出异常,正确做法是先用reader.IsDBNull(2)判断,或者用reader["age"] as int?这种可空类型的方式接收。这是实际开发中非常常见的坑。
四、事务处理与性能优化
当你需要批量插入大量数据时,一定会发现默认模式下SQLite的写入速度慢得惊人。原因是SQLite默认为每条语句自动开启并提交一个事务,每次提交都会触发磁盘同步操作。解决方法是把批量操作包在一个显式事务里,速度可以提升成百上千倍。
string connStr = "Data Source=test.db;Version=3;";
using (SQLiteConnection conn = new SQLiteConnection(connStr))
{
conn.Open();
using (SQLiteTransaction tran = conn.BeginTransaction())
{
try
{
string sql = "INSERT INTO users (name, age, email) VALUES (@name, @age, @email)";
using (SQLiteCommand cmd = new SQLiteCommand(sql, conn, tran))
{
cmd.Parameters.Add("@name", System.Data.DbType.String);
cmd.Parameters.Add("@age", System.Data.DbType.Int32);
cmd.Parameters.Add("@email", System.Data.DbType.String);
for (int i = 0; i < 10000; i++)
{
cmd.Parameters["@name"].Value = "用户" + i;
cmd.Parameters["@age"].Value = 20 + i % 30;
cmd.Parameters["@email"].Value = "user" + i + "@ipipp.com";
cmd.ExecuteNonQuery();
}
}
tran.Commit(); // 提交事务,所有插入一次性生效
Console.WriteLine("批量插入完成");
}
catch
{
tran.Rollback(); // 出错时回滚,保证数据一致性
throw;
}
}
}上面这个例子还用到了另一个优化技巧:循环外只创建一次SQLiteCommand并预先定义好参数,循环内只更新参数值。这省去了每次循环重复解析SQL语句的开销,配合事务使用效果最佳。如果不使用事务,插入一万条数据可能需要几十秒;包在事务里之后,通常一秒之内就能完成,差距非常明显。
除了事务,还有几个实用的优化点值得了解。连接字符串中可以加上Pooling=True启用连接池,减少频繁创建连接的开销;对经常出现在WHERE条件中的字段建立索引,例如CREATE INDEX idx_users_name ON users(name),能显著加快查询速度;如果应用场景允许极小的数据丢失风险,还可以加Synchronous=Off;Journal Mode=WAL参数,在写入性能和数据安全之间取得平衡。
最后提一下部署。程序发布时只需要带上生成的数据库文件和驱动DLL,目标机器上不需要安装任何数据库软件。数据库文件可以直接拷贝备份,也可以用SQLiteConnection.BackupDatabase方法在程序运行时做在线备份,这对需要定期归档数据的桌面应用来说非常方便。掌握了以上内容,你就已经能够应对绝大多数C#本地数据存储的需求了。
C#操作SQLiteSystem.Data.SQLite轻量级数据库修改时间:2026-09-06 10:10:37