.NET MAUI Android SQLite数据库无法正常重置,需重启应用
场景概述
开发基于.NET MAUI的Android应用,使用内置SQLite数据库,采用CommunityMVVM Toolkit和EntityFramework Core实现对象-数据库映射。将仅含表结构的空数据库文件放在Resources/Raw目录,应用启动时检查目标位置,若不存在则复制该文件(因捆绑资源为只读,需复制后才可修改)。
应用日常数据库交互正常,但执行重置功能时出现问题:修改数据后,调用删除并重建数据库的测试方法,读取数据时原修改仍保留,仅重启应用后重置才生效。
相关代码
测试命令代码
[RelayCommand] void PerformDatabaseFileTests() { ClearText(); try { AppendText($"DATABASE FILE EXISTS: {DatabaseFile.Exists()}"); AppendText("DELETING DB FILE..."); DatabaseFile.Delete(); AppendText($"DATABASE FILE EXISTS: {DatabaseFile.Exists()}"); AppendText("CREATING DB FILE..."); MainThread.InvokeOnMainThreadAsync(DatabaseFile.Create); AppendText($"DATABASE FILE EXISTS: {DatabaseFile.Exists()}"); } catch (Exception ex) { AppendText("EXCEPTION WAS THROWN!"); AppendText(ex.ToString()); AppendText(ex.Message); } }
DatabaseFile工具类
public static class DatabaseFile { public static string FullPath => Path.Combine(FileSystem.Current.AppDataDirectory, Filename); public static async Task Create() { using Stream inputStream = await FileSystem.Current.OpenAppPackageFileAsync(Filename); using FileStream outputStream = File.Create(FullPath); await inputStream.CopyToAsync(outputStream); } public static void Delete() { if (Exists()) File.Delete(FullPath); } public static bool Exists() => File.Exists(FullPath); }
DbContext配置代码
public partial class DatabaseContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); SqliteConnectionStringBuilder builder = new SqliteConnectionStringBuilder() { DataSource = Path.Combine(FileSystem.Current.AppDataDirectory, DatabaseFile.Filename), Mode = SqliteOpenMode.ReadWriteCreate, ForeignKeys = true }; optionsBuilder.UseSqlite(builder.ToString()); } }
核心疑问
- 是否错误认为DbContext实例释放时连接会自动关闭?
- 有没有更优的内置数据库部署方式?
解决方案
问题根源:SQLite连接未彻底释放
你的猜测正确——DbContext释放时,底层SQLite连接可能并未立即关闭。SQLite默认会缓存连接,若存在未释放的DbContext实例,或EF Core连接池持有连接,会导致原数据库文件被锁定,删除操作看似成功但未彻底释放资源,后续操作仍读取旧连接缓存中的数据,而非新复制的文件。
修复步骤
确保DbContext实例被正确释放
所有使用DbContext的场景必须用using语句包裹,确保实例及时释放:using(var dbContext = new DatabaseContext()) { // 数据库操作逻辑 }若使用依赖注入,保持DbContext的生命周期为默认的Scoped,避免长生命周期实例持续持有连接。
重置时强制释放所有SQLite连接
在删除数据库文件前,添加代码强制清除连接池并释放文件锁:// 清除EF Core连接池 SqliteConnection.ClearAllPools(); // 触发WAL日志检查点并关闭连接 using(var tempConn = new SqliteConnection($"Data Source={DatabaseFile.FullPath}")) { tempConn.Open(); tempConn.Execute("PRAGMA wal_checkpoint(FULL);"); }修正异步操作的等待问题
测试方法中调用MainThread.InvokeOnMainThreadAsync(DatabaseFile.Create)未等待,可能导致文件创建未完成就执行后续操作,需改为异步方法并等待:[RelayCommand] async Task PerformDatabaseFileTests() { ClearText(); try { AppendText($"DATABASE FILE EXISTS: {DatabaseFile.Exists()}"); AppendText("DELETING DB FILE..."); // 先强制释放连接 SqliteConnection.ClearAllPools(); using(var tempConn = new SqliteConnection($"Data Source={DatabaseFile.FullPath}")) { tempConn.Open(); tempConn.Execute("PRAGMA wal_checkpoint(FULL);"); } DatabaseFile.Delete(); AppendText($"DATABASE FILE EXISTS: {DatabaseFile.Exists()}"); AppendText("CREATING DB FILE..."); await MainThread.InvokeOnMainThreadAsync(DatabaseFile.Create); AppendText($"DATABASE FILE EXISTS: {DatabaseFile.Exists()}"); } catch (Exception ex) { AppendText("EXCEPTION WAS THROWN!"); AppendText(ex.ToString()); AppendText(ex.Message); } }
优化的数据库部署方式
使用EF Core迁移初始化数据库
无需预先打包空数据库文件,直接通过EF Core迁移在首次启动时生成表结构:- 定义实体和DbContext后,生成迁移:
dotnet ef migrations add InitialCreate - 应用启动时执行迁移:
using(var dbContext = new DatabaseContext()) { dbContext.Database.Migrate(); }
该方式更灵活,后续结构变更只需生成新迁移即可,无需替换资源文件。
- 定义实体和DbContext后,生成迁移:
预打包数据库的优化方案
若必须使用预打包数据库:- 首次复制文件后,执行
VACUUM操作优化数据库 - 复制前检查文件哈希值,避免重复复制损坏文件
- 首次复制文件后,执行
内容的提问来源于stack exchange,提问作者MaxKolac

