C# WinForm应用实战:SQLite数据库的配置、连接与CRUD操作详解
1. 环境准备与SQLite基础配置第一次接触SQLite时我被它的轻量级特性惊艳到了——整个数据库就是一个单独的文件不需要像SQL Server那样安装庞大的服务端。对于WinForm这类桌面应用来说SQLite简直是绝配。下面我会手把手带你完成环境搭建。先说说我的踩坑经历刚开始直接在NuGet里搜索SQLite结果装错了System.Data.SQLite.Core包导致后续操作各种报错。正确做法是安装System.Data.SQLite这个包注意不带.Core后缀。安装完成后记得在代码文件顶部添加这两个引用using System.Data; using System.Data.SQLite;有个实用技巧分享给大家我习惯在项目根目录下创建App_Data文件夹专门存放数据库文件。这样做有两个好处一是方便管理二是发布时可以直接复制整个文件夹。用DB Browser创建数据库时建议勾选自动递增主键选项否则后面插入数据时容易遇到主键冲突。2. 数据库连接的最佳实践连接字符串的配置看似简单但这里藏着不少门道。我曾经因为路径问题调试了整整一上午——相对路径在开发时能用发布后却报错。后来改用绝对路径就稳了SQLiteConnectionStringBuilder builder new SQLiteConnectionStringBuilder { DataSource Path.Combine(Application.StartupPath, App_Data\\mydb.db), Version 3, FailIfMissing false // 数据库不存在时自动创建 }; using (var conn new SQLiteConnection(builder.ToString())) { conn.Open(); // 后续操作... }实测发现加上FailIfMissingfalse参数特别实用。当数据库文件不存在时SQLite会自动创建新库省去了手动检查的麻烦。连接池的配置也很关键对于频繁操作数据库的应用建议设置Poolingtrue默认就是开启的。3. 增删改查(CRUD)完整实现3.1 数据插入的防注入写法新手最容易犯的错误就是直接拼接SQL字符串这样会有SQL注入风险。来看看我是怎么改造的public bool InsertUser(string name, int age) { const string sql INSERT INTO Users (Name, Age) VALUES (name, age); using (var cmd new SQLiteCommand(sql, _connection)) { cmd.Parameters.AddWithValue(name, name); cmd.Parameters.AddWithValue(age, age); return cmd.ExecuteNonQuery() 0; } }这里用了参数化查询name和age是占位符。有次我忘记调用ExecuteNonQuery()调试半天才发现数据没存进去所以现在都会检查返回值是否大于0。3.2 查询结果绑定DataGridView把查询结果直接显示到DataGridView控件上这个需求太常见了。经过多次优化我总结出最高效的绑定方式public void LoadUsersToGrid(DataGridView grid) { const string sql SELECT UserId, Name, Age FROM Users; var adapter new SQLiteDataAdapter(sql, _connection); var dataSet new DataSet(); adapter.Fill(dataSet, Users); grid.DataSource dataSet.Tables[Users]; // 可选设置列标题中文显示 grid.Columns[Name].HeaderText 姓名; grid.Columns[Age].HeaderText 年龄; }注意要调用Dispose()释放资源或者直接用using语句包裹。我遇到过内存泄漏问题后来发现是没及时释放DataAdapter导致的。4. 性能优化实战技巧当数据量达到10万条以上时普通的插入操作会变得特别慢。经过多次测试我找到了几个有效的优化方案首先开启事务批量插入速度能提升百倍using (var transaction _connection.BeginTransaction()) { try { for (int i 0; i 100000; i) { InsertUser($User{i}, i % 100); } transaction.Commit(); } catch { transaction.Rollback(); throw; } }其次调整SQLite的PRAGMA参数这三个配置组合效果最好_executeNonQuery(PRAGMA synchronousOFF); _executeNonQuery(PRAGMA journal_modeWAL); _executeNonQuery(PRAGMA cache_size5000);WAL模式特别适合多线程读写场景实测在并发操作时性能提升明显。缓存大小根据可用内存调整一般设为5000-10000页每页约1KB。5. 常见问题排查指南连接字符串正确但始终报无法打开数据库文件这种情况我遇到过好几次通常是以下原因文件被其他进程锁定比如DB Browser正在打开文件路径包含特殊字符尤其是中文路径没有写入权限Program Files目录需要管理员权限推荐使用这个诊断方法if(!File.Exists(dbPath)) { MessageBox.Show($数据库文件不存在{dbPath}); } else if(IsFileLocked(dbPath)) { MessageBox.Show(文件被其他进程锁定); } bool IsFileLocked(string filePath) { try { using (File.Open(filePath, FileMode.Open, FileAccess.ReadWrite)){} return false; } catch (IOException) { return true; } }另一个高频问题是中文乱码解决方法是在连接字符串中加入EncodingUTF8参数。如果使用DB Browser创建表记得把字符集也设为UTF-8。