TL; DR-我希望服务器而不是我的应用程序进行备份,因为服务器已设置为这样做,而我的应用程序将无权访问。
背景
[我公司20年前为客户创建了用Delphi 7 / Pascal编写的软件。我正在用C#重写软件。作为重写的一部分,我创建了新的Firebird,Oracle和SQL Server数据库。联邦法规要求维护所有现有数据,因此我创建了一个数据库修改/转换工具,以便从旧的数据库结构更改为新的数据库结构。
开始进行更改之前,我需要备份现有数据库。将运行此工具的技术人员无法访问其本地文件结构,也无法手动访问数据库所在的远程服务器。该工具访问本地系统上的类似.ini
的加密文件,以解析连接字符串的组成部分并创建连接对象。然后,我使用该连接对象连接到技术人员计算机要连接到的同一数据库。 这部分全部有效
如果我不理会默认备份路径,它将尝试备份到默认路径,但是会在本地计算机上(技术人员无权创建,并且我们也不希望技术人员也可以访问.bak)。修改默认备份路径为从连接字符串获取的网络路径,我得到
[SmoException:System.Data.SqlClient.SqlError:无法打开备份设备操作系统错误67(找不到网络名称。)。
因为文件路径不是网络共享(并且不会),并且数据库用户凭据无法从SQL Server外部访问该路径。
所以问题是:如何像在服务器上一样将备份备份到远程默认路径?
这里是产生上述错误的代码(对于远程情况,它为空)。>>
public static void FullSqlBackup (Connection oldProactiveSql) { String sqlServerLogin = oldProactiveSql.UserName; String password = oldProactiveSql.PassWord; String instanceName = oldProactiveSql.InstanceName; String remoteSvrName = oldProactiveSql.Ip + "," + oldProactiveSql.Port; Server srv2; Server srv3; string device; switch (oldProactiveSql.InstanceName) { case null: ServerConnection srvConn2 = new ServerConnection(remoteSvrName); srvConn2.LoginSecure = false; srvConn2.Login = sqlServerLogin; srvConn2.Password = password; srv3 = new Server(srvConn2); srv2 = null; Console.WriteLine(srv3.Information.Version); if (srv3.Settings.DefaultFile is null) { device = srv3.Information.RootDirectory + "\\DATA\\"; device = device.Substring(2); device = oldProactiveSql.Ip + device; } else device = srv3.Settings.DefaultFile; device = device.Substring(2); device = string.Concat("\\\\", oldProactiveSql.Ip, device); break; default: ServerConnection srvConn = new ServerConnection(); srvConn.ServerInstance = @".\" + instanceName; srvConn.LoginSecure = false; srvConn.Login = sqlServerLogin; srvConn.Password = password; srv2 = new Server(srvConn); srv3 = null; Console.WriteLine(srv2.Information.Version); if (srv2.Settings.DefaultFile is null) { device = srv2.Information.RootDirectory + "\\DATA\\"; } else device = srv2.Settings.DefaultFile; break; } Backup bkpDbFull = new Backup(); bkpDbFull.Action = BackupActionType.Database; bkpDbFull.Database = oldProactiveSql.DbName; bkpDbFull.Devices.AddDevice(device, DeviceType.File); bkpDbFull.BackupSetName = oldProactiveSql.DbName + " database Backup"; bkpDbFull.BackupSetDescription = oldProactiveSql.DbName + " database - Full Backup"; bkpDbFull.Initialize = true; bkpDbFull.PercentComplete += CompletionStatusInPercent; bkpDbFull.Complete += Backup_Completed; switch (oldProactiveSql.InstanceName) { case null: try { bkpDbFull.SqlBackup(srv3); } catch (Exception e) { Console.WriteLine (e); Console.WriteLine(e.InnerException.Message); throw; } break; default: try { bkpDbFull.SqlBackup(srv2); } catch (Exception e) { Console.WriteLine(e); Console.WriteLine(e.InnerException.Message); throw; } break; } }
任何帮助都将不胜感激,因为我现在正围着圈子跑。
根据下面的评论,我会尝试-1.在数据库上动态创建存储过程[BackupToDefault],然后运行它。2.如果失败,则将数据库链接到自身。3.尝试-在[LinkedSelfSynonmym]中执行[BackupToDefault]。
祝我好运,尽管它看起来很混乱,但很长的路要走,我希望它能起作用。
TL; DR-我希望服务器而不是我的应用程序进行备份,因为服务器已设置为这样做,而我的应用程序将无权访问。背景我公司为客户创建了20年的软件...
为了启发...将备份分为3个文件,每个文件位于不同的目录(SQL实例备份dir和sql实例默认dir和数据库主目录)
//// compile with:
// /r:Microsoft.SqlServer.Smo.dll
// /r:Microsoft.SqlServer.SmoExtended.dll
// /r:Microsoft.SqlServer.ConnectionInfo.dll
using System;
using System.Data;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
namespace SMObackup
{
class Program
{
static void Main()
{
// For remote connection, remote server name / ServerInstance needs to be specified
ServerConnection srvConn2 = new ServerConnection("machinename"/* <--default sql instance on machinename*/); // or (@"machinename\sqlinstance") for named instances
srvConn2.LoginSecure = false;
srvConn2.Login = "smologin";
srvConn2.Password = "SmoL@gin11";
srvConn2.DatabaseName = "msdb";
Server srv3 = new Server(srvConn2);
//server info
Console.WriteLine("servername:{0} ---- version:{1}", srv3.Name, srv3.Information.Version);
//server root directory
string serverRootDir = srv3.Information.RootDirectory;
//server backup directory
string serverBackupDir = srv3.Settings.BackupDirectory;
//database primary directory
string databasePrimaryFilepath = srv3.Databases[srvConn2.DatabaseName].PrimaryFilePath;
Console.WriteLine("server_root_dir:{0}\nserver_backup_dir:{1}\ndatabase_primary_dir{2}", serverRootDir, serverBackupDir, databasePrimaryFilepath);
Backup bkpDbFull = new Backup();
bkpDbFull.Action = BackupActionType.Database;
//comment out copyonly ....
bkpDbFull.CopyOnly = true; //copy only, just for testing....avoid messing up with existing backup processes
bkpDbFull.Database = srvConn2.DatabaseName;
//backup file name
string backupfile = $"\\backuptest_{DateTime.Now.ToString("dd/MM/yyyy/hh/mm/ss")}.bak";
//add multiple files, in each location
bkpDbFull.Devices.AddDevice(serverRootDir + backupfile, DeviceType.File);
bkpDbFull.Devices.AddDevice(serverBackupDir + backupfile, DeviceType.File);
bkpDbFull.Devices.AddDevice(databasePrimaryFilepath + backupfile, DeviceType.File);
bkpDbFull.Initialize = true;
foreach (BackupDeviceItem backupdevice in bkpDbFull.Devices)
{
Console.WriteLine("deviceitem:{0}", backupdevice.Name);
}
//backup is split/divided amongst the 3 devices
bkpDbFull.SqlBackup(srv3);
Restore restore = new Restore();
restore.Devices.AddRange(bkpDbFull.Devices);
DataTable backupHeader = restore.ReadBackupHeader(srv3);
//IsCopyOnly=True
for (int r = 0; r < backupHeader.Rows.Count; r++)
{
for (int c = 0; c < backupHeader.Columns.Count; c++)
{
Console.Write("{0}={1}\n", backupHeader.Columns[c].ColumnName, (string.IsNullOrEmpty(backupHeader.Rows[r].ItemArray[c].ToString())? "**": backupHeader.Rows[r].ItemArray[c].ToString()) );
}
}
srvConn2.Disconnect(); //redundant
}
}
}