关于 maui-sqlite-database
面向 .NET MAUI 应用的 SQLite 开发技能,整理平台差异与数据库实现要点,涵盖连接生命周期、路径、并发和性能。
主要功能
- 01跨平台数据库路径建议
- 02单例连接与延迟初始化
- 03WAL 模式与并发指导
- 04安全执行数据库文件操作
- 05查询与批量写入优化建议
适用场景
- 01选择适用于 MAUI 平台的数据库路径
- 02避免重复连接导致数据损坏
- 03规划批量写入和大表查询
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ davidortinau/ maui-skills --skill maui-sqlite-database安装后在 Claude Code 中输入/maui-sqlite-database确认已加载。
附带文件references/sqlite-database-api.md
详细介绍
SQLite 数据库 — 注意事项与最佳实践
有关完整的服务实现、常量、数据模型模板和常见模式,请参阅 references/sqlite-database-api.md。
⚠️ 错误的包选择陷阱
<PackageReference Include="Microsoft.Data.Sqlite" />
<PackageReference Include="sqlite-net" />
<PackageReference Include="SQLitePCL.raw" />
<PackageReference Include="sqlite-net-pcl" Version="1.9.*" />
<PackageReference Include="SQLitePCLRaw.bundle_green" Version="2.1.*" />
常见错误
❌ 使用 Environment.GetFolderPath 获取数据库路径
// ❌ Not cross-platform safe — fails on some MAUI targets
var path = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData), "app.db3");
// ✅ Use FileSystem.AppDataDirectory for all MAUI platforms
var path = Path.Combine(FileSystem.AppDataDirectory, "app.db3");
❌ 创建多个 SQLiteAsyncConnection 实例
多个指向同一文件的 SQLiteAsyncConnection 实例不是线程安全的。通过 DI 单例使用同一个实例:
// ❌ Creating new connections per request
public async Task<List<Item>> GetItems()
{
var db = new SQLiteAsyncConnection(Constants.DatabasePath);
return await db.Table<Item>().ToListAsync();
}
// ✅ Lazy singleton — one connection, created once
private SQLiteAsyncConnection? _database;
private async Task<SQLiteAsyncConnection> GetDatabaseAsync()
{
if (_database is not null) return _database;
_database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
await _database.ExecuteAsync("PRAGMA journal_mode=WAL;");
await _database.CreateTableAsync<TodoItem>();
return _database;
}
❌ 忘记启用 WAL 模式
未启用 WAL 时,读取操作会阻塞写入操作。初始化时务必启用:
await _database.ExecuteAsync("PRAGMA journal_mode=WAL;");
❌ 对已打开的数据库执行文件操作
// ❌ Moving/deleting while connection is open — data corruption
File.Delete(Constants.DatabasePath);
// ✅ Always close first
await databaseService.CloseConnectionAsync();
if (File.Exists(Constants.DatabasePath))
File.Delete(Constants.DatabasePath);
平台注意事项
| 平台 | 注意事项 |
|---|---|
| iOS | FileSystem.AppDataDirectory 由 iCloud 备份 — 使用 FileSystem.CacheDirectory,将数据库排除在 iCloud 备份之外 |
| 所有平台 | 多个 SQLiteAsyncConnection 实例指向同一文件 → 数据损坏 |
| 所有平台 | 未启用 WAL → 读取操作阻塞写入操作,并发性能不佳 |
| 所有平台 | 对已打开的数据库执行文件操作 → 数据损坏 |
决策指南
| 问题 | 建议 |
|---|---|
| DI 生命周期? | 单例 — 使用一个连接,WAL 负责处理并发读取 |
| WAL 模式? | 始终启用 — 在移动设备上没有理由不启用 |
| 数据库路径? | FileSystem.AppDataDirectory — 绝不要使用 Environment.GetFolderPath |
| 保存模式? | 检查 Id != 0 → 更新,否则插入 |
| 多个表? | 在延迟初始化中添加所有 CreateTableAsync<T>() 调用 |
| 需要导出/备份? | 先关闭连接,然后调用 File.Copy |
性能提示
- 批量写入时使用事务 — 单独插入速度较慢;使用
RunInTransactionAsync包裹 - 为经常查询的列添加
[Indexed]— 尤其是外键 - WAL 模式可消除读取与写入之间的争用
- 避免对大型表使用
ToListAsync()— 使用Where()筛选并分页 - 复杂查询使用原始 SQL — 对于联接,
QueryAsync<T>比链式 LINQ 更快
检查清单
- 安装
sqlite-net-pcl+SQLitePCLRaw.bundle_green(而不是Microsoft.Data.Sqlite) - 数据库路径使用
FileSystem.AppDataDirectory - 模型带有
[PrimaryKey, AutoIncrement] - 使用单个
DatabaseService,采用延迟异步初始化模式 - 通过
PRAGMA journal_mode=WAL启用 WAL - 在 DI 中将
DatabaseService注册为单例 - 移动、复制或删除任何文件之前关闭连接
- iOS:如有需要,将数据库排除在 iCloud 备份之外
评论
支持 Markdown