You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MAUI Android SQLite数据库无法正常重置,需重启应用

.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());
    }
}

核心疑问

  1. 是否错误认为DbContext实例释放时连接会自动关闭?
  2. 有没有更优的内置数据库部署方式?

解决方案

问题根源:SQLite连接未彻底释放

你的猜测正确——DbContext释放时,底层SQLite连接可能并未立即关闭。SQLite默认会缓存连接,若存在未释放的DbContext实例,或EF Core连接池持有连接,会导致原数据库文件被锁定,删除操作看似成功但未彻底释放资源,后续操作仍读取旧连接缓存中的数据,而非新复制的文件。

修复步骤

  1. 确保DbContext实例被正确释放
    所有使用DbContext的场景必须用using语句包裹,确保实例及时释放:

    using(var dbContext = new DatabaseContext())
    {
        // 数据库操作逻辑
    }
    

    若使用依赖注入,保持DbContext的生命周期为默认的Scoped,避免长生命周期实例持续持有连接。

  2. 重置时强制释放所有SQLite连接
    在删除数据库文件前,添加代码强制清除连接池并释放文件锁:

    // 清除EF Core连接池
    SqliteConnection.ClearAllPools();
    // 触发WAL日志检查点并关闭连接
    using(var tempConn = new SqliteConnection($"Data Source={DatabaseFile.FullPath}"))
    {
        tempConn.Open();
        tempConn.Execute("PRAGMA wal_checkpoint(FULL);");
    }
    
  3. 修正异步操作的等待问题
    测试方法中调用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);
        }
    }
    

优化的数据库部署方式

  1. 使用EF Core迁移初始化数据库
    无需预先打包空数据库文件,直接通过EF Core迁移在首次启动时生成表结构:

    • 定义实体和DbContext后,生成迁移:
      dotnet ef migrations add InitialCreate
      
    • 应用启动时执行迁移:
      using(var dbContext = new DatabaseContext())
      {
          dbContext.Database.Migrate();
      }
      

    该方式更灵活,后续结构变更只需生成新迁移即可,无需替换资源文件。

  2. 预打包数据库的优化方案
    若必须使用预打包数据库:

    • 首次复制文件后,执行VACUUM操作优化数据库
    • 复制前检查文件哈希值,避免重复复制损坏文件

内容的提问来源于stack exchange,提问作者MaxKolac

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 18:46:16