3

我正在为应用程序编写一个简单的数据库备份和恢复例程。我可以毫无问题地备份我的数据库,但是当我恢复时,我无法获得对我的数据库的独占访问权限。

我正在尝试对 SO 进行所有修复组合,将其置于单用户模式,使其脱机,然后仅将其放回去,但没有成功。我可以在 studio manager (express) 中成功恢复数据库

这个方法是当时唯一连接到SQL server的,所以我不明白为什么不能执行restore。

感谢您指出问题所在的帮助。

internal void RestoreDatabase(string databaseFile)
        {
            //get database details
            var databaseConfiguration = new DatabaseConfiguration().GetDatabaseConfiguration();

            try
            {
                //construct server connection string
                var connection = databaseConfiguration.IsSqlAuthentication
                                     ? new ServerConnection(databaseConfiguration.ServerInstance,
                                                            databaseConfiguration.SqlUsername,
                                                            databaseConfiguration.SqlPassword)
                                     : new ServerConnection(databaseConfiguration.ServerInstance);

                //set database to single user and kick everyone off
                using (
                    var sqlconnection =
                        new SqlConnection(new DatabaseConfiguration().MakeConnectionString(databaseConfiguration)))
                {
                    sqlconnection.Open();
                    using (
                        var sqlcommand = new SqlCommand("ALTER DATABASE " + databaseConfiguration.DatabaseName + " SET Single_User WITH Rollback IMMEDIATE",
                                                        sqlconnection))
                    {                       
                        sqlcommand.ExecuteNonQuery();                        
                    }
                    using (
                        var sqlcommand = new SqlCommand("ALTER DATABASE " + databaseConfiguration.DatabaseName + " SET OFFLINE",
                                                        sqlconnection))
                    {
                        sqlcommand.ExecuteNonQuery();
                    }
                    using (
                        var sqlcommand = new SqlCommand("ALTER DATABASE  " + databaseConfiguration.DatabaseName + "  SET ONLINE",
                                                        sqlconnection))
                    {
                        sqlcommand.ExecuteNonQuery();
                    }
                    sqlconnection.Close();
                }

                //setup server connection and restore
                var server = new Server(connection);
                var restore = new Restore();
                restore.Database = databaseConfiguration.DatabaseName;
                restore.Action = RestoreActionType.Database;
                restore.Devices.AddDevice(databaseFile, DeviceType.File);
                restore.ReplaceDatabase = true;
                restore.Complete += Restore_Complete;
                restore.SqlRestore(server);
            }
            catch (Exception ex)
            {
                //my bad
                restoreDatabaseServerError(ex.InnerException.Message, EventArgs.Empty);
            }
            finally
            {
                //set database to multi user
                using (
                    var sqlconnection =
                        new SqlConnection(new DatabaseConfiguration().MakeConnectionString(databaseConfiguration)))
                {
                    sqlconnection.Open();
                    using (
                        var sqlcommand = new SqlCommand("ALTER DATABASE " + databaseConfiguration.DatabaseName + " SET Multi_User",
                                                        sqlconnection))
                    {
                        sqlcommand.ExecuteNonQuery();
                        sqlcommand.Dispose();
                    }
                    sqlconnection.Close();
                }
            }

        }
4

2 回答 2

3

如果有人连接到您的数据库,SQL Server 无法删除它,因此您必须断开现有连接,就像您尝试过的那样。single_user 的问题是,它仍然允许单个用户连接。由于您自己在删除数据库时无法连接到数据库,因此您必须离开那里。这会打开该插槽供其他人连接,从而防止您丢弃它。

有一些 SQL Server 进程特别擅长在那一瞬间连接到数据库。复制就是一个例子。(无论如何,您都不应该真正删除已发布的数据库,蝙蝠那是另一回事。)

那么我们能做些什么呢?唯一 100% 安全的方法是阻止用户连接到数据库。唯一实用的方法是将数据库切换到脱机状态,然后将其删除。但是,这有一个令人讨厌的副作用,即 SQL Server 不会删除该数据库的文件,因此您必须手动执行此操作。

另一种选择是足够快。在您的示例中,您在删除数据库之前将其重新联机。这是一个相当耗费资源的过程,它给了“入侵者”很多时间来连接。

我一直在成功使用的解决方案如下所示:

ALTER DATABASE MyDb SET RESTRICTED_USER WITH ROLLBACK IMMEDIATE;
USE MyDb;
ALTER DATABASE MyDb SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
USE tempdb;
DROP DATABASE MyDb;

这首先将数据库设置为受限用户并连接到它。然后,虽然仍然连接,但它将数据库设置为单个用户。之后,上下文切换到 tempdb,然后立即执行 drop。这里重要的是,将这些命令作为一批发送到 SQL Server,以最大限度地USE tempdb;减少DROP. 在开始时将数据库设置为受限用户会遇到一些罕见的边缘情况,因此即使乍一看没有意义,也要保留它。

虽然这仍然给其他人留下了理论上的空白,但我从未见过它失败。

删除数据库后,您可以正常运行还原。

祝你好运。

于 2013-03-19T23:04:10.693 回答
0

您的恢复需要在您将数据库服务器设置为单用户模式的同一连接上进行。

总结以下更改,我将 using 的结尾移至还原代码下方,并将 SQL 连接的关闭移至还原后,以便它使用相同的连接。还删除了离线和在线设置,因为它们不需要。目前无法测试,所以让我知道它是否有效。

  //set database to single user and kick everyone off
  using (var sqlconnection = new SqlConnection(new DatabaseConfiguration().MakeConnectionString(databaseConfiguration)))
  {
    sqlconnection.Open();
    using (var sqlcommand = new SqlCommand("ALTER DATABASE " + databaseConfiguration.DatabaseName + " SET Single_User WITH Rollback IMMEDIATE",sqlconnection))
    {
        sqlcommand.ExecuteNonQuery();                        
    }
    //setup server connection and restore
    var server = new Server(sqlconnection);
    var restore = new Restore();
    restore.Database = databaseConfiguration.DatabaseName;
    restore.Action = RestoreActionType.Database;
    restore.Devices.AddDevice(databaseFile, DeviceType.File);
    restore.ReplaceDatabase = true;
    restore.Complete += Restore_Complete;
    restore.SqlRestore(server);
    sqlconnection.Close();
  }
于 2013-03-19T23:03:50.450 回答