sql server备份还原必须由sql server引擎执行,c#仅传递restore database命令;需确保服务账户对备份路径有读取权限、显式重定位文件、数据库处于单用户模式,并正确处理路径、权限及状态要求。

SQL Server 备份文件还原必须用 RESTORE DATABASE,C# 本身不提供“数据库还原 API”
很多人误以为 C# 有类似 SqlBackup 那样的 SqlRestore 类——其实没有。Microsoft.SqlServer.Management.Smo(SMO)库确实封装了还原逻辑,但它底层仍是拼接并执行 T-SQL 的 RESTORE DATABASE 命令。直接调用 SQL Server 引擎才是唯一可靠路径。
常见错误现象:SqlRestore 构造后调用 SqlRestore.SqlRestore(server) 却报错 System.Data.SqlClient.SqlException: Cannot open backup device ... Operating system error 5(Access is denied.)——这通常不是 C# 代码问题,而是 SQL Server 服务账户无权访问备份文件路径。
- 还原操作必须在 SQL Server 实例上下文中执行,C# 只是客户端发起者
- 备份文件路径需对 SQL Server 服务账户(而非当前 Windows 登录用户)可读
- 若用 UNC 路径(如
\fileserverackupsmydb.bak),SQL Server 必须以域账户运行,且该账户有共享+NTFS 权限 - 本地路径(如
D:ackupmydb.bak)也要确保 SQL Server 服务账户对该盘符有读取权限(默认 Local System 无法读取多数用户目录)
SMO 还原时必须显式设置 RelocateFiles,否则大概率报错 File 'xxx' cannot be restored to 'xxx.mdf'
即使目标数据库不存在,SMO 默认仍尝试将数据/日志文件还原到原备份时的物理路径。若原路径在另一台机器上不存在(比如 C:OldServerData),或权限不足,就会失败。这不是警告,是硬性报错。
正确做法是遍历 SqlRestore.ReadFileList(server) 获取逻辑文件名,再为每个文件新建 RelocateFile 实例:
var dbFiles = restore.ReadFileList(server);
foreach (DataRow row in dbFiles.Rows)
{
string logicalName = row["LogicalName"].ToString();
string physicalName = row["PhysicalName"].ToString();
string ext = Path.GetExtension(physicalName).ToLower();
string newPath = ext == ".mdf"
? @"D:SQLDataMyDb.mdf"
: @"D:SQLLogMyDb.ldf";
restore.RelocateFiles.Add(new RelocateFile(logicalName, newPath));
}
-
RelocateFile中的logicalName必须与ReadFileList()返回的完全一致(大小写敏感) - 目标文件夹(如
D:SQLData)必须预先存在,SQL Server 不会自动创建目录 - 若还原到新数据库名(
restore.Database = "NewDbName"),仍需重定位,因为逻辑文件名来自原备份,不会自动改名
还原前必须确保数据库处于单用户模式或已离线,否则报错 Exclusive access could not be obtained because the database is in use
SQL Server 不允许在数据库被其他连接占用时执行还原。C# 代码里不能靠“等几秒再试”解决——必须主动踢掉现有连接。
- 推荐做法:先执行
ALTER DATABASE [MyDb] SET SINGLE_USER WITH ROLLBACK IMMEDIATE,再还原,最后SET MULTI_USER - 若目标库还不存在(首次还原),此步可跳过;但若库存在且正在使用,缺这步必失败
- SMO 的
SqlRestore.NoRecovery = false(默认)表示还原后数据库可立即使用;设为true则保持 RESTORING 状态,适合后续日志还原,但此时库不可连 - 注意事务:
SET SINGLE_USER和RESTORE应放在同一连接中执行,避免中间被其他连接抢占
用 SqlConnection + SqlCommand 执行 RESTORE 更轻量,但需手动处理路径转义和权限校验
不用 SMO 也能还原——只要拼出合法的 T-SQL RESTORE DATABASE 语句并执行。优势是部署简单(无需引用 SMO 程序集),劣势是错误反馈更原始、文件路径需手动处理。
典型语句结构:
RESTORE DATABASE [MyDb] FROM DISK = N'D:ackupmydb.bak' WITH FILE = 1, MOVE N'MyDb_Data' TO N'D:SQLDataMyDb.mdf', MOVE N'MyDb_Log' TO N'D:SQLLogMyDb.ldf', REPLACE, RECOVERY;
-
N''前缀必须加,否则中文路径或特殊字符会出错 -
REPLACE选项用于强制覆盖已存在的同名数据库(否则报错 “database already exists”) -
FILE = 1指备份集序号,一个 .bak 文件可能含多个备份(FULL + LOG),需用RESTORE HEADERONLY查看 - 执行前建议先用
RESTORE VERIFYONLY校验备份完整性,避免还原中途失败
底层原理就一句话:所有还原动作都由 SQL Server 引擎完成,C# 只是传递指令和参数。真正难的从来不是写哪行 C# 代码,而是搞清 SQL Server 对路径、权限、状态的硬性要求——这些地方错一点,整个还原就卡住。










