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

C# 调用SMO还原SQL Server数据库执行查询校验后失败如何解决

问题根因

  • 核心原因是ADO.NET默认启用连接池机制:你在CheckDatabase方法中虽然显式调用了Close()和Dispose()处理数据库连接,但该操作只是将连接放回连接池复用,并没有真正断开和SQL Server的物理连接。此时目标数据库仍然存在活动会话,而SQL Server还原数据库需要独占访问权限,存在活动连接时就会触发还原失败的报错。
  • 次要问题:RestoreDatabase方法中创建的SqlConnection没有手动释放,也会残留活动连接;CheckDatabase中重复写了资源释放逻辑,using语句已经会在代码块退出时自动释放托管资源,finally块中的重复释放属于冗余操作。

解决方案

1. 清空连接池彻底释放残留连接

在执行还原操作前调用SqlConnection.ClearAllPools(),清空所有连接池中的连接,彻底断开之前的活动会话。

2. 还原前设置数据库为单用户模式

还原数据库前先杀掉目标库的所有活动进程,设置为单用户模式,确保还原操作能获得独占访问权,还原完成后再改回多用户模式。

3. 优化冗余代码

给DataReader也加上using自动释放,抛出异常时直接用throw而非throw ex避免丢失原始堆栈信息,删除多余的手动释放逻辑。

4. 修复未释放的连接

RestoreDatabase中的SqlConnection用using包裹,用完立刻释放。

修改后的代码示例

CheckDatabase方法优化

private static void CheckDatabase(string period)
{
    string connString = Config.ConnectionStrings.ConnectionStrings["connString"].ConnectionString;
    string controlProcesoCommand = "select top 5 * from ControlProceso order by FechaInicio desc;";
    // using自动释放连接和命令对象,不需要额外手动释放
    using (SqlConnection conn = new SqlConnection(connString))
    using (SqlCommand command = new SqlCommand(controlProcesoCommand, conn))
    {
        try
        {
            conn.Open();
            // reader也用using自动释放,不需要手动Close
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    int lastPeriodDb = int.Parse(reader["Periodo"].ToString());
                    int actualPeriod = int.Parse(period);

                    if (period.Equals(reader["Periodo"].ToString()))
                        throw new Exception(Resources.Messages.Period_already_processed);
                    else if (lastPeriodDb > actualPeriod)
                        throw new Exception(Resources.Messages.Period_saved_after_actual_period);
                    else break;
                }
            }
        }
        catch (Exception ex)
        {
            // 直接throw保留原始异常堆栈,不要写throw ex
            throw;
        }
    }
}

RestoreDatabase方法优化

private static void RestoreDatabase()
{
    string connString = Config.ConnectionStrings.ConnectionStrings["connString"].ConnectionString;
    string dbName;
    string dataSource;
    
    // 用using包裹连接,用完立刻释放
    using (SqlConnection conn = new SqlConnection(connString))
    {
        dbName = conn.Database;
        dataSource = conn.DataSource;
    }
    // 清空所有连接池,彻底释放之前残留的活动连接
    SqlConnection.ClearAllPools();
    
    string restoreFilePath = Config.AppSettings.Settings["RestoreFilePath"].Value;
    Server myServer = new Server(dataSource);
    
    try
    {
        // 如果目标数据库存在,先杀所有进程,设置为单用户模式
        if (myServer.Databases.Contains(dbName))
        {
            myServer.KillAllProcesses(dbName);
            myServer.Databases[dbName].DatabaseOptions.UserAccess = DatabaseUserAccess.Single;
            myServer.Databases[dbName].Alter(TerminationClause.RollbackTransactionsImmediately);
        }

        Restore dbRestore = new Restore();
        dbRestore.Action = RestoreActionType.Database;
        dbRestore.Database = dbName;
        dbRestore.Devices.AddDevice(restoreFilePath, DeviceType.File);
        dbRestore.ReplaceDatabase = true;
        dbRestore.SqlRestore(myServer);

        // 还原完成后改回多用户模式
        if (myServer.Databases.Contains(dbName))
        {
            myServer.Databases[dbName].DatabaseOptions.UserAccess = DatabaseUserAccess.Multiple;
            myServer.Databases[dbName].Alter();
        }
    }
    catch (Exception ex)
    {
        // 异常时尝试恢复为多用户模式,避免后续无法访问数据库
        if (myServer.Databases.Contains(dbName) 
            && myServer.Databases[dbName].DatabaseOptions.UserAccess == DatabaseUserAccess.Single)
        {
            myServer.Databases[dbName].DatabaseOptions.UserAccess = DatabaseUserAccess.Multiple;
            myServer.Databases[dbName].Alter();
        }
        throw;
    }
}

可选优化

如果你不想全局清空连接池,也可以在CheckDatabase使用的连接字符串末尾加上Pooling=false,这样该连接用完就会直接物理断开,不会进入连接池。

内容的提问来源于stack exchange,提问作者Bruno Gonzalez Torres

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 05:36:02