手动删除.bak后SQL Server数据库卸载重装异常问题咨询
问题背景
我们有一个通过C#在Microsoft SQL Server 2012中安装2个数据库的程序,卸载时数据库未被删除;重装时程序会检查并删除现有数据库后重建,.bak文件存储在C:/Program Files或对应目录。若卸载后手动删除.bak文件,首次重装失败,但尝试三次后可成功创建数据库。
相关C#代码:
String path = System.Reflection.Assembly.GetExecutingAssembly().Location; path = System.IO.Path.GetDirectoryName(path); Server server = InitializeServer(log); server.ConnectionContext.Connect(); Database db = server.Databases["ADB"]; if (db != null) { try { db.Drop();//-------------------------First try fails here } catch (Exception ex) { throw new ApplicationException(ex.Message); } } try //----------------- second try database ADB gets created { Restore restore = new Restore(); // Set type of backup to be performed to database restore.Database = "ADB"; restore.Action = RestoreActionType.Database; restore.ReplaceDatabase = true; // Create the Restore database ldf & mdf file name String dataFileLocation = path + "ADB" + ".mdf"; String logFileLocation = path + "ADB" + "_Log.ldf"; RelocateFile rf = new RelocateFile("ADB", dataFileLocation); // Set up the backup device to use filesystem. restore.Devices.AddDevice(path + "\\ADB.bak", DeviceType.File); System.Data.DataTable logicalRestoreFiles = restore.ReadFileList(server); restore.RelocateFiles.Add(new RelocateFile(logicalRestoreFiles.Rows[0][0].ToString(), dataFileLocation)); restore.RelocateFiles.Add(new RelocateFile(logicalRestoreFiles.Rows[1][0].ToString(), logFileLocation)); restore.NoRecovery = false; restore.SqlRestore(server); } catch (Exception ex) { throw new ApplicationException(ex.Message); } // Installing 2nd database---------------------------------------------- server.ConnectionContext.Connect(); Database db1 = server.Databases["BDB"]; if (db1 != null) { try { db1.Drop();//-------------------------Second try fails here } catch (Exception ex) { throw new ApplicationException(ex.Message); } } try //----------------- Third try database BDB gets created { Restore restore2 = new Restore(); restore2.Database = "BDB"; restore2.Action = RestoreActionType.Database; restore2.ReplaceDatabase = true; String dataFileLocation = path + "BDB" + ".mdf"; String logFileLocation = path + "BDB" + "_Log.ldf"; RelocateFile rf = new RelocateFile("BDB", dataFileLocation); restore2.Devices.AddDevice(path + "\\BDB.bak", DeviceType.File); System.Data.DataTable logicalRestoreFiles = restore2.ReadFileList(server); restore2.RelocateFiles.Add(new RelocateFile(logicalRestoreFiles.Rows[0][0].ToString(), dataFileLocation)); restore2.RelocateFiles.Add(new RelocateFile(logicalRestoreFiles.Rows[1][0].ToString(), logFileLocation)); restore2.NoRecovery = false; restore2.SqlRestore(server); }
删除数据库时的报错信息:
Microsoft.SqlServer.Management.Smo.FailedOperationException: Drop failed for Database 'ADB'. ---> Microsoft.SqlServer.Management.Common.ExecutionFailureException: An exception occurred while executing a Transact-SQL statement or batch. ---> System.Data.SqlClient.SqlException: Cannot drop the database 'ADB', because it does not exist or you do not have permission. at Microsoft.SqlServer.Management.Common.ConnectionManager.ExecuteTSql(ExecuteTSqlAction action, Object execObject, DataSet fillDataSet, Boolean catchException) at Microsoft.SqlServer.Management.Common.ServerConnection.ExecuteNonQuery(String sqlCommand, ExecutionTypes executionType) --- End of inner exception stack trace --- at Microsoft.SqlServer.Management.Common.ServerConnection.ExecuteNonQuery(String sqlCommand, ExecutionTypes executionType) at Microsoft.SqlServer.Management.Common.ServerConnection.ExecuteNonQuery(StringCollection sqlCommands, ExecutionTypes executionType) at Microsoft.SqlServer.Management.Smo.ExecutionManager.ExecuteNonQuery(StringCollection queries) at Microsoft.SqlServer.Management.Smo.SqlSmoObject.ExecuteNonQuery(StringCollection queries, Boolean includeDbContext) at Microsoft.SqlServer.Management.Smo.SqlSmoObject.DropImplWorker(Urn& urn) at Microsoft.SqlServer.Management.Smo.SqlSmoObject.DropImpl() --- End of inner exception stack trace --- at Microsoft.SqlServer.Management.Smo.SqlSmoObject.DropImpl() at SetupScripts.SetupDataBase.Execute(FrmLog log)
解答
1. 手动删除.bak文件后能否分离服务器数据库?
完全可以。.bak文件只是数据库的备份文件,它和SQL Server实例中存在的数据库是完全独立的两个实体。分离数据库是SQL Server对实例内数据库的管理操作,核心是把数据库从SQL Server的元数据列表中移除,但保留数据库的.mdf和.ldf物理文件,只要满足以下条件就能执行:
- 数据库当前没有被用户或进程占用
- 你拥有执行分离操作的权限
- 数据库确实存在于SQL Server实例中
这个操作和.bak文件是否存在没有任何关联,你遇到的删除数据库报错,本质是程序读取的元数据和实际数据库状态不一致,和备份文件无关。
2. 为何第二次尝试能成功创建数据库?
结合你的代码和报错信息,核心原因是SQL Server SMO的元数据缓存问题,加上重试过程中程序自动恢复了.bak文件,具体拆解:
- 首次重装失败的原因:
代码通过server.Databases["ADB"]检查数据库是否存在时,SMO可能使用了缓存的元数据,误以为数据库还存在(但实际上卸载后数据库可能已被隐性删除,或者权限问题导致SMO读取了错误状态)。当执行db.Drop()时,SQL Server实际执行DROP DATABASE语句,发现数据库根本不存在,于是抛出Cannot drop the database 'ADB'...的错误,导致首次流程终止。 - 第二次尝试成功的原因:
重试时SMO的元数据缓存已经更新,此时server.Databases["ADB"]会返回null,代码直接跳过Drop()逻辑,进入数据库恢复流程。同时,你的重装程序在重试过程中应该已经重新将.bak文件复制到了指定路径,所以Restore操作能正常读取备份文件,成功创建数据库。
另外,建议优化代码:在Drop()的catch块中判断异常类型,如果是“数据库不存在”的预期错误,可以直接跳过Drop()步骤继续执行Restore,这样能避免首次重试的失败。
内容的提问来源于stack exchange,提问作者Madz
相关产品推荐
相关产品推荐

