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

ASP.NET Core 6中SQLite备份文件锁定,上传Azure Blob失败

问题原因分析

问题出在SQLite的连接池机制上。尽管你通过using块确保了SqliteConnection被正确释放,但SQLite默认启用连接池:当你Dispose连接时,它不会立即关闭底层的文件句柄,而是将连接放回连接池供后续复用。这就导致备份文件仍然被连接池占用,因此上传时抛出文件无法访问的异常。

解决方案

方法1:禁用备份连接的连接池(最简单直接)

在备份数据库的连接字符串中添加Pooling=false,这样Dispose连接时会直接关闭文件句柄,不会将连接放回连接池。

修改后的代码:

public void CreateBackup()
{
    string backupPath = string.Empty;
    // 为备份连接添加Pooling=false参数
    var backupConnectionString = $"{configuration.GetConnectionString("BackupDatabase")};Pooling=false";
    
    using (var originalConnection = new SqliteConnection(configuration.GetConnectionString("Database")))
    using (var backupConnection = new SqliteConnection(backupConnectionString))
    {
        originalConnection.Open();
        originalConnection.BackupDatabase(backupConnection);
        backupPath = backupConnection.DataSource;
    }

    var blobClient = new BlobClient(configuration.GetConnectionString("AzureBackup"), "tanusitvanytar", $"database-{DateTime.Now:yyyy-MM-dd}");
    blobClient.Upload(backupPath);
}

方法2:备份到临时文件并自动清理(避免长期占用本地文件)

如果不想修改连接池配置,可以先备份到临时文件,上传完成后立即删除临时文件,同时确保临时文件的连接禁用连接池:

public void CreateBackup()
{
    string tempBackupPath = Path.GetTempFileName();
    try
    {
        using (var originalConnection = new SqliteConnection(configuration.GetConnectionString("Database")))
        using (var backupConnection = new SqliteConnection($"Data Source={tempBackupPath};Pooling=false"))
        {
            originalConnection.Open();
            backupConnection.Open();
            originalConnection.BackupDatabase(backupConnection);
        }

        // 上传临时文件到Azure Blob
        var blobClient = new BlobClient(configuration.GetConnectionString("AzureBackup"), "tanusitvanytar", $"database-{DateTime.Now:yyyy-MM-dd}");
        using (var fileStream = File.OpenRead(tempBackupPath))
        {
            blobClient.Upload(fileStream);
        }
    }
    finally
    {
        // 无论上传成功与否,都删除临时文件
        if (File.Exists(tempBackupPath))
        {
            File.Delete(tempBackupPath);
        }
    }
}

方法3:备份到内存流(完全避免本地文件操作)

跳过本地文件,直接将数据库备份到内存流,再上传到Azure Blob,彻底消除文件占用问题:

public void CreateBackup()
{
    using (var originalConnection = new SqliteConnection(configuration.GetConnectionString("Database")))
    {
        originalConnection.Open();
        // 创建内存中的备份连接
        using (var backupConnection = new SqliteConnection("Data Source=:memory:"))
        {
            backupConnection.Open();
            originalConnection.BackupDatabase(backupConnection);

            // 将内存中的数据库写入临时文件流(SQLite暂不支持直接备份到Stream,需中转)
            string tempPath = Path.GetTempFileName();
            try
            {
                using (var tempConn = new SqliteConnection($"Data Source={tempPath};Pooling=false"))
                {
                    tempConn.Open();
                    backupConnection.BackupDatabase(tempConn);
                }

                // 读取临时文件流并上传
                var blobClient = new BlobClient(configuration.GetConnectionString("AzureBackup"), "tanusitvanytar", $"database-{DateTime.Now:yyyy-MM-dd}");
                using (var stream = File.OpenRead(tempPath))
                {
                    blobClient.Upload(stream);
                }
            }
            finally
            {
                if (File.Exists(tempPath)) File.Delete(tempPath);
            }
        }
    }
}

注意事项

  • 若使用Microsoft.Data.Sqlite,可以调用SqliteConnection.ClearAllPools()强制清空所有连接池,但这会影响所有正在使用的SQLite连接,仅在确认无其他活跃连接时使用。
  • 建议优先使用方法1或方法2,代码简洁且能有效解决问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 04:13:14