添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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

3 人关注

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

我尝试了所有修复SO的组合,把它放在单用户模式下,脱机后再放回去,都没有成功。 我可以在工作室管理器(Express)中成功恢复数据库。

这种方法是当时与SQL服务器的唯一连接,所以我不明白为什么我不能执行还原。

感谢帮助,指出问题可能出在哪里。

internal void RestoreDatabase(string databaseFile)
            //get database details
            var databaseConfiguration = new DatabaseConfiguration().GetDatabaseConfiguration();
                //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();
    
8 个评论
nkvu
如果这条评论没有帮助,很抱歉,但另一种方法是使用SMO(希望能为你解决一些管道方面的问题)。请看 这个 这个 的一般方法,以及 这个 关于备份/恢复的具体例子....,如果这个评论没有帮助,请再次抱歉。
Damo
我的例子中使用的是SMO?
为什么这个标签是WPF?
Damo
因为它是一个使用Microsoft.SqlServer.Management.Common和Microsoft.SqlServer.Management.Smo的WPF应用程序。
@Damo老兄,你在那里发布的所有代码都与WPF有关 nothing 。你的问题是与 SQL ,而不是 WPF
Damo
移除WPF标签。我已经习惯于提供太多的信息了,我想
nkvu
对不起,我看错了问题。出于好奇,如果你不使用SMO,但在设置了单用户后(在你打开sqlconnetion的情况下,你手动发出RESTORE DATABASE命令(好吧,使用SqlCommand),然后你设置多用户,会发生什么?
Damo
在使用SQL Profiler的进一步调查中,我可能有另一个对象保持我的数据库开放......现在来追踪它...
c#
sql-server-2008
Damo
Damo
发布于 2013-03-20
2 个回答
Sebastian Meine
Sebastian Meine
发布于 2013-03-20
已采纳
0 人赞同

如果有人连接到你的数据库,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 之间的时间。在开始时将数据库设置为受限用户,可以捕捉到一些罕见的边缘情况,所以即使它乍一看没有意义,也要保留它。

虽然这仍然留下了一个理论上的缺口,让其他人进入,但我从未见过它失败。

在数据库被丢弃后,你可以像平常一样运行你的恢复。

祝你好运。

Damo
是的,这样就可以了。谢谢你。
uNople
uNople
发布于 2013-03-20
0 人赞同

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

综上所述,我把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;