C# SQL Резервное копирование сервера удаленной базы данных в удаленное хранилище по умолчанию без прямого доступа к удаленному хранилищу? - PullRequest
0 голосов
/ 04 февраля 2020

TL; DR - я хочу, чтобы сервер делал резервную копию, а не мое приложение, потому что сервер настроен для этого, и мое приложение не будет иметь доступа.

Фон

Моя компания создала программное обеспечение для клиентов 20 лет go, написанное в Delphi 7 / Pascal. Я переписываю программное обеспечение в C#. В рамках переписывания я создал новые серверные базы данных Firebird, Oracle и SQL. Федеральное регулирование требует, чтобы все существующие данные были сохранены, поэтому я создал инструмент модификации / преобразования базы данных, чтобы перейти от старой структуры базы данных к новой.

Прежде чем начать вносить изменения, мне нужно сделать резервную копию существующей базы данных. Технический специалист, который будет запускать этот инструмент, не имеет доступа к своей локальной файловой структуре и не имеет ручного доступа к удаленному серверу, на котором размещена база данных. Инструмент обращается к зашифрованному .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. Попробуйте - Exe c [BackupToDefault] На [LinkedSelfSynonmym]

Wi sh мне повезет, хотя это кажется запутанным и долгий путь, я надеюсь, что это работает.

Ответы [ 2 ]

0 голосов
/ 05 февраля 2020

Спасибо @SeanLange

Уверен, вам нужно будет использовать Dynami c sql в этом случае. Тогда путь будет относительно места выполнения sql.

Я изменил свой код, добавив:

 private static void WriteBackupSp (Server remoteServer, string dbName, out string storedProcedure)
      {
           var s = "CREATE PROCEDURE [dbo].[ProactiveDBBackup]\n";
           s += "AS\n";
           s += "BEGIN\n";
           s += "SET NOCOUNT ON\n";
           s += "BACKUP DATABASE " + dbName + " TO DISK = \'" + string.Concat (remoteServer.BackupDirectory,@"\", dbName, ".bak") + "\'\n";
           s += "END\n";
           storedProcedure = s;
      }

, затем изменил последний переключатель на:

switch (oldProactiveSql.InstanceName)
           {
                case null:
                     try
                     {
                          WriteBackupSp (srv3, oldProactiveSql.DbName, out var storedProcedure);
                          ConnectionToolsUtility.GenerateSqlConnectionString (oldProactiveSql, out var cs);

                          using (SqlConnection connection = new SqlConnection (cs))
                          {
                               using (SqlCommand command = new SqlCommand (storedProcedure, connection))
                               {
                                    connection.Open ();
                                    command.ExecuteNonQuery ();
                                    connection.Close ();
                               }
                          }
                          var execBackup = "EXEC [dbo].[ProactiveDBBackup]\n";
                          using (SqlConnection connection = new SqlConnection (cs))
                          {
                               using (SqlCommand command = new SqlCommand (execBackup, connection))
                               {
                                    connection.Open ();
                                    command.ExecuteNonQuery ();
                                    connection.Close ();
                               }
                          }
                     }
                     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;
           }

И это позволило мне сделать резервную копию базы данных через строку подключения в месте резервного копирования по умолчанию, не имея учетных данных с доступом к расположению сетевого пути.

0 голосов
/ 05 февраля 2020

Для вдохновения ... резервная копия разделена на 3 файла, каждый из которых находится в отдельном каталоге (sql каталог для резервных копий экземпляра и sql каталог для экземпляра по умолчанию и основной каталог базы данных)

//// 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

        }
    }
}
...