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

EF Core与SQLite报错:尝试写入只读数据库问题排查

问题根源

异常提示"attempt to write a readonly database",且调用栈指向EF Core的AcquireDatabaseLock方法,说明加密转明文操作后,数据库文件被未释放的连接锁定,或者转换后的数据库状态异常,导致EF Core执行迁移时无法获取写权限。

修复步骤

1. 重构加密转明文方法,确保连接完全释放

当前GetDecryptedConnection方法返回打开的连接,会持续持有文件句柄,导致后续操作无法写入。改成一次性转换方法,用using自动释放连接:

private void ConvertEncryptedToPlainText(string plainTextDbPath, string encryptedDbPath)
{                
    using (var connection = new SqliteConnection($"Filename={encryptedDbPath};Mode=ReadWriteCreate"))
    {
        connection.Open();
                
        // 参数化设置密码,避免注入和转义问题
        var command = connection.CreateCommand();
        command.CommandText = "PRAGMA key = @password;";
        command.Parameters.AddWithValue("@password", _password);
        command.ExecuteNonQuery();
        command.Parameters.Clear();
                
        using (var cmd = connection.CreateCommand())
        {
            cmd.CommandText = "PRAGMA cipher_compatibility = 3;";
            cmd.ExecuteNonQuery();
        }
                
        command.CommandText = $"ATTACH DATABASE '{plainTextDbPath}' AS {PlainText} KEY '';";
        command.ExecuteNonQuery();

        command.CommandText = $"SELECT sqlcipher_export('{PlainText}');";
        command.ExecuteScalar();

        command.CommandText = $"DETACH DATABASE {PlainText};";
        command.ExecuteNonQuery();
    }
    // using块结束后,连接自动关闭,文件锁释放
}

2. 修正明文连接的初始化逻辑

转换后的明文数据库可能残留加密相关的PRAGMA设置,需要在连接时清空:

private SqliteConnection GetPlainTextConnection(string databasePath)
{
    var connection = new SqliteConnection($"Filename={databasePath};Mode=ReadWriteCreate;");
    connection.Open();

    // 清空可能残留的密钥设置,确保数据库处于明文状态
    using (var cmd = connection.CreateCommand())
    {
        cmd.CommandText = "PRAGMA key = '';";
        cmd.ExecuteNonQuery();
    }

    return connection;
}

3. 调整EF Core迁移触发逻辑

异常发生在Migrate阶段,说明转明文后的数据库可能缺失迁移历史表,或者EF重复执行迁移锁操作。修改EncryptedDbContext.Create()方法,避免每次实例化都执行Migrate:

public static void Create()
{
    using (var context = new EncryptedDbContext())
    {
        // 仅当没有已应用的迁移时才执行迁移
        if (!context.Database.GetAppliedMigrations().Any())
        {
            context.Database.Migrate();
        }
    }
}

4. 检查跨平台文件权限

虽然你确认文件系统访问正常,但转换后的明文数据库可能继承了加密数据库的权限:

  • Android:确保MAUI项目的Platforms/Android/AndroidManifest.xml中包含必要的存储权限,针对Android 10+使用Scoped Storage
  • iOS:在Platforms/iOS/Info.plist中添加NSDocumentsFolderUsageDescription,说明应用需要访问Documents目录的原因
验证步骤
  1. 执行加密转明文操作后,重启应用(确保所有旧连接释放)
  2. 尝试添加多条数据,确认异常消失
  3. 检查明文数据库文件的读写权限是否正确

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 09:59:53