简介:本资源是一个基于C# WinForm的SQLite数据库操作完整示例项目,面向.NET初学者与桌面应用开发者,聚焦数据安全与基础CRUD实践。项目实现了带密码保护的SQLite数据库创建、连接、增删改查等核心功能,并封装了SQLiteHelper工具类与DbOperator业务逻辑层,便于快速集成到实际项目中。压缩包共37个文件,包含10个C#源码文件(如Form1.cs、SQLiteHelper.cs)、2个可执行exe、2个核心DLL(含System.Data.SQLite.dll)、以及csproj/sln工程配置和resx/config等配套资源,整体体积仅1.08MB,轻量易部署。已有4645人学习下载,代码结构清晰、模块职责分明,附带完整VS解决方案与调试配置,开箱即用,适合用于教学演示、课程设计或小型本地数据管理工具开发参考。
1. 一个能直接编译运行的 C# SQLite 增删改查加密数据库 Demo,不是教程拼凑,而是生产级最小可行闭环
你不需要再翻三份文档、改五次连接字符串、试七种密码格式,才能让 C# 程序真正把数据「安全地存进 SQLite 文件」。这个标题指向的不是一个教学幻灯片,而是一套可立即复制粘贴、带完整异常防护、支持 AES-256 加密、兼容 .NET 6+ 的本地数据库操作闭环。它解决的是上位机软件、工业采集终端、离线桌面工具中真实存在的痛点:既要轻量(SQLite 单文件),又要合规(敏感字段或整库加密),还要稳定(事务回滚、连接池复用、SQL 注入防御)。新手能双击Program.cs编译即跑通插入/查询;五年经验的开发者会关注KeyDerivation.Pbkdf2盐值长度、PRAGMA cipher_page_size对 WAL 模式的影响、以及SQLiteConnectionStringBuilder中Password参数与sqlite3_key的底层映射关系。它不依赖 Entity Framework Core 的抽象层,也不用第三方 ORM 封装——所有 SQL 执行、参数绑定、密钥派生、错误码解析都暴露在代码里,方便你嵌入到现有 WinForms/WPF/MAUI 项目中。
2. 用 Microsoft.Data.Sqlite 在 .NET 6+ 中创建并初始化加密 SQLite 数据库
SQLite 本身不原生支持加密,必须通过扩展(如 SQLCipher)实现。但 .NET 生态中,Microsoft.Data.Sqlite官方包已内置对 SQLCipher 的支持——前提是安装对应原生库。这一步是整个 Demo 的基石:没有正确加载加密驱动,后续所有CREATE TABLE或INSERT都会因SQLite error 26: 'file is encrypted or is not a database'失败。
2.1 安装必要 NuGet 包与原生依赖
在项目文件(.csproj)中添加以下依赖:
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.8" /> <PackageReference Include="SQLitePCLRaw.bundle_e_sqlcipher" Version="2.1.12" />注意:
bundle_e_sqlcipher是关键。它打包了 Windows/macOS/Linux 下预编译的 SQLCipher 动态库(e_sqlcipher.dll/libe_sqlcipher.dylib/libe_sqlcipher.so),避免手动部署.so或.dll。若使用bundle_green或bundle_blue,则无法启用加密功能。
2.2 构建带密码的连接字符串并初始化数据库文件
加密数据库的创建不是“先建库再设密”,而是在首次打开连接时,通过连接字符串传递密码,由 SQLCipher 自动完成密钥派生与页头加密。以下代码创建一个名为secure.db的加密数据库,并建表:
using Microsoft.Data.Sqlite; string dbPath = "secure.db"; string password = "MySecurePassw0rd!"; // 实际项目中应从配置或密钥管理服务获取 // 使用 SQLiteConnectionStringBuilder 构造连接字符串 var csb = new SqliteConnectionStringBuilder { DataSource = dbPath, Password = password, // 此参数触发 SQLCipher 密钥设置 Mode = SqliteOpenMode.ReadWriteCreate }; using var connection = new SqliteConnection(csb.ToString()); connection.Open(); // 启用 WAL 模式提升并发写入性能(加密库支持) using var cmd = connection.CreateCommand(); cmd.CommandText = "PRAGMA journal_mode = WAL;"; cmd.ExecuteNonQuery(); // 创建用户表(含主键、非空约束、默认时间戳) cmd.CommandText = @" CREATE TABLE IF NOT EXISTS Users ( Id INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT NOT NULL, Email TEXT UNIQUE, CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP );"; cmd.ExecuteNonQuery();关键参数说明:
Password = password:这是 SQLCipher 的入口。Microsoft.Data.Sqlite会将该值传给sqlite3_key()函数,内部调用 PBKDF2-HMAC-SHA256 进行密钥派生(默认 64000 次迭代)。Mode = SqliteOpenMode.ReadWriteCreate:确保文件不存在时自动创建,存在时以读写方式打开。PRAGMA journal_mode = WAL:WAL(Write-Ahead Logging)模式在加密数据库中仍有效,且比 DELETE 模式更适合多线程读写场景。验证方式:执行PRAGMA journal_mode;返回wal。
2.3 验证数据库是否真正加密
仅靠连接成功不能证明加密生效。最可靠的方法是用十六进制编辑器打开secure.db文件 —— 未加密的 SQLite 文件开头是明文SQLite format 3\0;加密后前 16 字节为随机密文(如0x7A 0x2F 0x9E 0x1C...)。更工程化的验证方式是:新建一个无密码连接尝试读取:
try { using var testConn = new SqliteConnection($"Data Source={dbPath};"); testConn.Open(); // 此处必抛 SqliteException,Message 含 "file is encrypted" } catch (SqliteException ex) when (ex.SqliteErrorCode == 26) { Console.WriteLine("✅ 加密验证通过:无法无密访问"); }3. C# 中安全执行 SQLite 增删改查的四步闭环实现
增删改查不是简单拼接 SQL 字符串。本节提供一套兼顾安全性、可读性、事务一致性的标准模式:参数化查询 + 显式事务 + 异常分类处理 + 结果强类型映射。所有操作均基于Microsoft.Data.Sqlite原生 API,不引入额外抽象层。
3.1 插入(Insert):防止 SQL 注入与空值陷阱
public static int InsertUser(string name, string email) { const string sql = @" INSERT INTO Users (Name, Email) VALUES (@name, @email);"; using var connection = new SqliteConnection(csb.ToString()); connection.Open(); using var transaction = connection.BeginTransaction(); using var cmd = connection.CreateCommand(); cmd.Transaction = transaction; cmd.CommandText = sql; // 参数化绑定,自动处理 NULL 和类型转换 cmd.Parameters.Add(new SqliteParameter("@name", name ?? string.Empty)); cmd.Parameters.Add(new SqliteParameter("@email", email ?? string.Empty)); try { int rowsAffected = cmd.ExecuteNonQuery(); transaction.Commit(); return rowsAffected; // 返回影响行数,非自增 ID } catch (SqliteException ex) when (ex.SqliteErrorCode == 19) // SQLITE_CONSTRAINT { throw new InvalidOperationException($"邮箱 {email} 已存在,违反唯一约束"); } catch { transaction.Rollback(); throw; } }参数设计逻辑:
@name和@email使用SqliteParameter而非字符串插值,彻底杜绝' OR 1=1--类注入。name ?? string.Empty避免null传入导致SqliteException(SQLite 的 TEXT 类型允许 NULL,但业务逻辑常需空字符串兜底)。SqliteErrorCode == 19捕获唯一约束冲突(UNIQUE constraint failed),便于前端提示“邮箱已被注册”。
3.2 查询(Select):按条件检索并映射到 C# 对象
public static List<User> FindUsersByName(string keyword) { const string sql = @" SELECT Id, Name, Email, CreatedAt FROM Users WHERE Name LIKE @keyword ORDER BY CreatedAt DESC;"; using var connection = new SqliteConnection(csb.ToString()); connection.Open(); using var cmd = connection.CreateCommand(); cmd.CommandText = sql; cmd.Parameters.Add(new SqliteParameter("@keyword", $"%{keyword}%")); var users = new List<User>(); using var reader = cmd.ExecuteReader(); while (reader.Read()) { users.Add(new User { Id = reader.GetInt32(0), Name = reader.GetString(1), Email = reader.IsDBNull(2) ? null : reader.GetString(2), CreatedAt = reader.GetDateTime(3) }); } return users; } public class User { public int Id { get; set; } public string Name { get; set; } = string.Empty; public string? Email { get; set; } public DateTime CreatedAt { get; set; } }查询优化点:
LIKE @keyword使用参数化,%由 C# 拼接而非 SQL 内置,避免LIKE '%'+@keyword+'%'引发的参数嗅探失效。reader.IsDBNull(2)显式判断Email是否为 NULL,避免GetString(2)抛InvalidCastException。ORDER BY CreatedAt DESC确保最新记录优先返回,符合多数 UI 列表需求。
3.3 更新(Update)与删除(Delete):原子性与软删除实践
public static bool UpdateUserEmail(int userId, string newEmail) { const string sql = "UPDATE Users SET Email = @email WHERE Id = @id;"; using var connection = new SqliteConnection(csb.ToString()); connection.Open(); using var cmd = connection.CreateCommand(); cmd.CommandText = sql; cmd.Parameters.Add(new SqliteParameter("@email", newEmail)); cmd.Parameters.Add(new SqliteParameter("@id", userId)); return cmd.ExecuteNonQuery() > 0; // 返回 true 表示更新成功(至少一行) } public static bool DeleteUser(int userId) { // 生产环境推荐软删除:UPDATE SET IsDeleted = 1 const string sql = "DELETE FROM Users WHERE Id = @id;"; using var connection = new SqliteConnection(csb.ToString()); connection.Open(); using var cmd = connection.CreateCommand(); cmd.CommandText = sql; cmd.Parameters.Add(new SqliteParameter("@id", userId)); return cmd.ExecuteNonQuery() > 0; }提示:硬删除(
DELETE)不可逆。若业务需审计或恢复,应添加IsDeleted BOOLEAN DEFAULT 0字段,并将查询改为WHERE IsDeleted = 0。
4. SQLite 加密数据库的密钥管理与性能调优关键参数
加密不是加个密码就万事大吉。SQLCipher 提供多个 PRAGMA 指令控制加密强度、内存使用和兼容性。这些参数必须在数据库首次创建后、任何 DML 操作前设置,否则无效。
4.1 必调的 3 个加密相关 PRAGMA 参数
| PRAGMA 指令 | 推荐值 | 作用说明 | 修改时机 |
|---|---|---|---|
PRAGMA cipher_default_kdf_iter | 64000 | 密钥派生迭代次数。值越高越安全(防暴力破解),但首次打开慢。SQLCipher 默认值,无需修改 | 首次连接后立即执行 |
PRAGMA cipher_page_size | 4096 | 加密页大小(字节)。必须与 SQLite 默认页大小一致(4096),否则PRAGMA integrity_check失败 | 创建表前执行 |
PRAGMA cipher_hmac_algorithm | HMAC_SHA512 | HMAC 算法。SHA512 比默认 SHA1 更抗碰撞,但计算开销略高 | 创建表前执行 |
设置代码示例(紧接在CREATE TABLE之前):
cmd.CommandText = "PRAGMA cipher_page_size = 4096;"; cmd.ExecuteNonQuery(); cmd.CommandText = "PRAGMA cipher_hmac_algorithm = HMAC_SHA512;"; cmd.ExecuteNonQuery(); // 验证设置生效 cmd.CommandText = "PRAGMA cipher_hmac_algorithm;"; var hmac = cmd.ExecuteScalar()?.ToString(); // 应返回 "HMAC_SHA512"4.2 性能瓶颈排查:为什么加密后查询变慢?
加密本身带来 CPU 开销,但常见卡顿源于误配。以下是三个高频原因及对策:
| 现象 | 根本原因 | 解决方案 |
|---|---|---|
| 首次打开数据库耗时 >2 秒 | cipher_default_kdf_iter过高(如 256000)且密码复杂 | 保持默认 64000;密码无需过长,12 位含大小写字母+数字足够 |
| 多线程写入时频繁死锁 | 未启用 WAL 模式,所有写操作阻塞读 | PRAGMA journal_mode = WAL;(已在第 2 节设置) |
| 大量小查询响应延迟 | 每次操作新建连接,未复用连接池 | 使用SqliteConnection的连接池(默认开启),避免using var conn = new ...在循环内创建 |
注意:
Microsoft.Data.Sqlite的连接池默认启用,最大连接数 100。若看到SQLite error 5: 'database is locked',优先检查是否未释放SqliteCommand或SqliteDataReader,而非盲目调大连接池。
4.3 加密数据库迁移:如何安全升级密码或算法?
SQLCipher 不支持在线修改密码。必须导出明文数据 → 创建新库 → 用新密码导入。官方推荐流程:
- 用旧密码打开库:
PRAGMA key = 'old_pass'; - 导出为 SQL 文本:
.dump > backup.sql(通过sqlite3CLI 工具) - 用新密码创建空库,执行
backup.sql - 验证新库可读,删除旧库
C# 中无法直接调用.dump,需借助System.Diagnostics.Process调用sqlite3.exe:
var psi = new ProcessStartInfo("sqlite3.exe", $"\"{oldDbPath}\" .dump") { RedirectStandardOutput = true, UseShellExecute = false }; using var proc = Process.Start(psi); string dumpSql = proc.StandardOutput.ReadToEnd(); proc.WaitForExit(); // 创建新库并执行 dumpSql(需拆分语句,跳过 `PRAGMA` 行)5. 用 DB Browser for SQLite 验证加密状态与调试查询
开发阶段离不开可视化工具。DB Browser for SQLite(DB4S)是唯一免费开源、支持 SQLCipher 的 GUI 工具(v3.12.2+)。它不替代代码,而是帮你确认:加密是否生效?索引是否命中?查询计划是否合理?
5.1 配置 DB Browser 支持 SQLCipher 加密
- 下载 DB Browser for SQLite 官网版本 (必须 v3.12.2 或更高)
- 打开软件 →
File→Open Database→ 选择你的secure.db - 弹窗提示「Database is encrypted」→ 输入密码
MySecurePassw0rd! - 成功加载后,左侧表树显示
Users,双击可浏览数据
注意:若提示「Wrong password or not a database file」,检查:① DB4S 版本是否 ≥3.12.2;② 密码是否与 C# 中
Password=参数完全一致(区分大小写、空格);③ 数据库文件未被其他进程独占锁定。
5.2 用「Execute SQL」标签页调试复杂查询
在 DB4S 中编写并测试 SQL,避免在 C# 中反复修改调试:
-- 查看用户总数与最近注册者 SELECT COUNT(*) as Total, (SELECT Name FROM Users ORDER BY CreatedAt DESC LIMIT 1) as LatestUser FROM Users; -- 分析查询执行计划(关键!) EXPLAIN QUERY PLAN SELECT * FROM Users WHERE Email = 'test@example.com';EXPLAIN QUERY PLAN输出类似:
0|0|0|SEARCH TABLE Users USING COVERING INDEX sqlite_autoindex_Users_1 (Email=?)表示命中唯一索引sqlite_autoindex_Users_1(SQLite 自动为UNIQUE字段创建),效率为 O(log N)。若输出SCAN TABLE Users,则说明缺少索引,需手动添加:
CREATE INDEX idx_users_email ON Users(Email);5.3 导出/导入数据:跨环境同步加密数据库
DB4S 的「File」→「Export」→「Table to SQL file」可导出带INSERT语句的 SQL 脚本,供测试环境初始化。但注意:导出的 SQL不包含加密信息,导入到新库时需重新设置密码。
实际操作步骤:
- 在 DB4S 中选中
Users表 → 右键 →Export→Table to SQL file→ 保存为users_data.sql - 新建空加密库(用新密码打开)→ 执行
users_data.sql中所有INSERT语句 - 验证:
SELECT COUNT(*) FROM Users;返回预期行数
此流程确保测试数据与生产加密强度一致,且无需暴露原始密码给测试人员。
用dotnet run编译运行这个 Demo,你会得到一个真实可用的加密 SQLite 操作闭环:从文件生成、密码保护、参数化增删改查,到工具验证与性能调优。它不教你怎么学 C#,而是给你一把开箱即用的钥匙——插进锁孔,拧动,门就开了。
本文还有配套的精品资源,点击获取