无法恢复SQL数据库,无法获得独占访问权限(单用户模式)

时间:2013-03-19 22:40:44

标签: c# sql-server-2008

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

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

此方法是当时与SQL服务器的唯一连接,因此我不明白为什么我无法执行还原。

感谢帮助指出问题所在。

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

        }

2 个答案:

答案 0 :(得分: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之间的时间。在开始时将数据库设置为受限用户会捕获一些罕见的边缘情况,所以即使它在乍看之下没有意义,也要保留它。

虽然这仍然为其他人留下了理论上的空白,但我从来没有看到它失败过。

删除数据库后,您可以正常运行恢复。

祝你好运。

答案 1 :(得分:0)

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

总结下面的更改,我将使用结束移动到恢复代码下方,并将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();
  }