2016-08-15 8 views
2

Я создаю консольное приложение C#, которое восстанавливает базу данных SQL Server с предварительно определенными параметрами, которые могут быть изменены в меню коммутатора. Основное внимание в этом приложении простота. Он предназначен для всех, кто сможет восстановить базу данных SQL Server нажатием одной кнопки. Да, должен быть кто-то, кто может выполнить эту задачу грамотно, но есть много способов сделать вещи ... Независимо от того, что я построил логику, чтобы прокрутить резервные каталоги и выбрать правильные файлы резервных копий на основе точки во время. Все работает, кроме фактической части восстановления.C# - Восстановление базы данных SQL Server с помощью Inline SQL

Из других вопросов, которые я прочитал, я боюсь, что невозможно запустить SQL из C#, который таким образом модифицирует сервер. Я не хочу использовать объекты SMO, потому что это связано с дополнительными сложностями, которые я хочу избежать, но если это единственный способ сделать это, то это то, что я буду делать.

Всякий раз, когда я пытаюсь запустить этот код, он жалуется, что значение @dbName недействительно.

public static void RestoreDatabase(string Server_Name, string Instance_Name, string DB_Name, FileInfo BakFile, FileInfo DiffFile, FileInfo TrnFile, DateTime Point_In_Time) 
{ 
    SqlConnection conn = new SqlConnection(); 
    conn.ConnectionString = "Data Source=" + Server_Name + "\\" + Instance_Name + ";Initial Catalog=master;Integrated Security=True"; 

    string SqlQuery = @"ALTER DATABASE @dbName 
         SET SINGLE_USER 
         WITH ROLLBACK IMMEDIATE; 

         RESTORE DATABASE @dbName 
         FROM DISK = @BakFilePath 

         WITH NORECOVERY, REPLACE; 

         RESTORE DATABASE @dbName 
         FROM DISK = @DiffFilePath 
         WITH NORECOVERY; 

         RESTORE DATABASE @dbName 
         FROM DISK = @TrnFilePath 
         WITH RECOVERY, STOPAT = @RecoveryTime; 

         ALTER DATABASE @dbName 
         SET MULTI_USER;"; 

    SqlCommand cmd = new SqlCommand(); 
    cmd.CommandType = System.Data.CommandType.Text; 
    cmd.Connection = conn; 
    cmd.CommandText = SqlQuery; 

    try 
    { 
     cmd.Parameters.Add(new SqlParameter("@dbName", SqlDbType.NVarChar, 30)); 
     cmd.Parameters.Add(new SqlParameter("@BakFilePath", SqlDbType.NVarChar, 255)); 
     cmd.Parameters.Add(new SqlParameter("@DiffFilePath", SqlDbType.NVarChar, 255)); 
     cmd.Parameters.Add(new SqlParameter("@TrnFilePath", SqlDbType.NVarChar, 255)); 
     cmd.Parameters.Add(new SqlParameter("@RecoveryTime", SqlDbType.DateTime)); 
     cmd.Parameters["@dbName"].Value = DB_Name; 
     cmd.Parameters["@BakFilePath"].Value = BakFile.FullName; 
     cmd.Parameters["@DiffFilePath"].Value = DiffFile.FullName; 
     cmd.Parameters["@TrnFilePath"].Value = TrnFile.FullName; 
     cmd.Parameters["@RecoveryTime"].Value = Point_In_Time; 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex); 
     Console.ReadLine(); 
    } 

    try 
    { 
     Console.WriteLine("Restoring {0}...", DB_Name); 

     cmd.Connection.Open(); 
     cmd.ExecuteNonQuery(); 
     cmd.Connection.Close(); 

     Console.WriteLine("Restore Complete!"); 
     Console.ReadLine(); 
    } 
    catch (SqlException ex) 
    { 
     Console.WriteLine("Connection could not open. Error: {0}", ex); 
     Console.ReadLine(); 
    }  
} 

8/15/2016 10:38

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

Я попытался создать динамический SQL. Это был мой код, однако я не тратил много времени на проверку ошибок, поэтому я снова заберу этот маршрут и попытаюсь устранить любые ошибки.

string SqlQuery = string.Format(@"DECLARE @dbName NVARCHAR(MAX), @strSQL NVarchar(MAX)=N''; 
          SET @dbName = {0}; 

          SELECT 
          @strSQL += 'DECLARE @BakFilePath nvarchar(255) = N''{1}'',' 
          + N' @DiffFilePath nvarchar(255) = N''{2}'',' 
          + N' @TrnFilePath nvarchar(255) = N''{3}'',' 
          + N' @RecoveryTime DateTime = N''{4}''' 
          + N' ALTER DATABASE '+ @dbName 
          + N' SET SINGLE_USER' 
          + N' WITH ROLLBACK IMMEDIATE;' 
          + N' RESTORE DATABASE ' + @dbName 
          + N' FROM DISK = @BakFilePath' 
          + N' WITH NORECOVERY, REPLACE;' 
          + N' RESTORE DATABASE ' + @dbName 
          + N' FROM DISK = @DiffFilePath' 
          + N' WITH NORECOVERY;' 
          + N' RESTORE DATABASE ' + @dbName 
          + N' FROM DISK = @TrnFilePath' 
          + N' WITH RECOVERY, STOPAT = @RecoveryTime;' 
          + N' ALTER DATABASE ' + @dbName 
          + N' SET MULTI_USER; 
          ' 
          EXEC sp_executesql @strSQL",DB_Name, BakFile.FullName, DiffFile.FullName, TrnFile.FullName, Point_In_Time); 
+1

Не могли бы вы показать весь вывод «жалобы» на значение dbName? –

+0

В настоящее время вы пытаетесь выяснить, как вывести выход моей программы на экран и файл ... очень легко сделать на Linux ха-ха. –

+0

C# консольная программа? console.writeline(), и если вы импортируете system.windows.forms, вы можете использовать messagebox.show(), system.file.io вы можете использовать File.WriteAllText (путь к файлу). вы также можете при запуске вашей программы использовать синтаксис командной строки, такой как c: \ program.exe> ​​results.txt, чтобы сохранить его в файле. – Matt

ответ

0

Благодарим вас за ответы каждого, они ведут меня в направлении выбора динамического SQL. Ниже приведен код SQL, который работал для меня. Я объясню, что происходит, потому что святая корова осложняется (по крайней мере, для меня). Мое C# приложение вызывает функцию и я прохожу пять параметров:

  1. имя базы данных
  2. Полного файл резервной копии путь
  3. Дифференциального файл резервной копии путь
  4. транзакционного файл резервной копии путь
  5. момент время в восстановить до

Эти значения затем используются в определениях переменных SQL String. Как только переменные установлены в строке SQL, он становится регулярным динамическим SQL. Динамический SQL по-прежнему представляет собой огромную боль; выяснение всех одинарных кавычек вызвало у меня много горя. Как только динамический SQL отформатирован правильно, он похож на любой другой оператор SQL, который выполняется с C#.

ОБНОВЛЕНИЕ: Модифицированное решение для включения параметров для входных значений по запросу Scott Chamberlains и включает всю функцию в качестве решения.

государственной статической силы RestoreDatabase (строка имя_сервера, строка instance_name, строка db_name, FileInfo BakFile, FileInfo DiffFile, FileInfo TrnFile, DateTime Point_In_Time) { SqlConnection Conn = новый SqlConnection(); conn.ConnectionString = "Источник данных =" + Server_Name + "\" + Instance_Name + "; Initial Catalog = master; Integrated Security = True";

string SqlQuery = string.Format(@"DECLARE @strSQL NVARCHAR(MAX) ='' 
            SELECT 
            @strSQL += N'ALTER DATABASE ' + QUOTENAME(@dbName) 
            + N' SET SINGLE_USER' 
            + N' WITH ROLLBACK IMMEDIATE;' 
            + N' RESTORE DATABASE ' + QUOTENAME(@dbName) 
            + N' FROM DISK = N''' + @BakFilePath + '''' 
            + N' WITH NORECOVERY, REPLACE;' 
            + N' RESTORE DATABASE ' + QUOTENAME(@dbName) + 
            + N' FROM DISK = N''' + @DiffFilePath + '''' 
            + N' WITH NORECOVERY;' 
            + N' RESTORE DATABASE ' + QUOTENAME(@dbName) 
            + N' FROM DISK = N''' + @TrnFilePath + '''' 
            + N' WITH NORECOVERY, STOPAT = N''' + @RecoveryTime + ''';' 
            + N' RESTORE DATABASE ' + QUOTENAME(@dbName) 
            + N' WITH RECOVERY;' 
            + N' ALTER DATABASE ' + QUOTENAME(@dbName) 
            + N' SET MULTI_USER; 
            ' 
            EXEC sp_executesql @strSQL"); 

    SqlCommand cmd = new SqlCommand(); 
    cmd.CommandType = System.Data.CommandType.Text; 
    cmd.Connection = conn; 
    cmd.CommandText = SqlQuery; 
    try 
    { 
     cmd.Parameters.Add(new SqlParameter("@dbName", SqlDbType.NVarChar, 30)); 
     cmd.Parameters.Add(new SqlParameter("@BakFilePath", SqlDbType.NVarChar, 255)); 
     cmd.Parameters.Add(new SqlParameter("@DiffFilePath", SqlDbType.NVarChar, 255)); 
     cmd.Parameters.Add(new SqlParameter("@TrnFilePath", SqlDbType.NVarChar, 255)); 
     cmd.Parameters.Add(new SqlParameter("@RecoveryTime", SqlDbType.NVarChar,30)); 
     cmd.Parameters["@dbName"].Value = DB_Name; 
     cmd.Parameters["@BakFilePath"].Value = BakFile.FullName; 
     cmd.Parameters["@DiffFilePath"].Value = DiffFile.FullName; 
     cmd.Parameters["@TrnFilePath"].Value = TrnFile.FullName; 
     cmd.Parameters["@RecoveryTime"].Value = Point_In_Time.ToString(); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex); 
     Console.WriteLine("Press any key to exit"); 
     Console.ReadLine(); 
    } 
    try 
    { 
     Console.WriteLine("Restoring {0}...", DB_Name); 
     cmd.Connection.Open(); 
     cmd.ExecuteNonQuery(); 
     cmd.Connection.Close(); 
     Console.WriteLine("Restore Complete!"); 
     Console.WriteLine("Press any key to exit"); 
     Console.ReadLine(); 
    } 
    catch (SqlException ex) 
    { 
     Console.WriteLine("Connection could not open. Error: {0}", ex); 
     Console.ReadLine(); 
    } 

} 
+1

Ваши объявления являются сверхбезопасными, избавиться от них и вернуться к использованию' SqlParameter' как вы делали в своем первоначальном вопросе. но затем используйте их в сочетании с кодом, который у вас есть прямо сейчас (все после «SELECT» в порядке) –

4

К сожалению, есть проблема с тем, как написан ваш SQL. Я дала ответ на вопрос, который был подобный основной вопрос, вы можете найти его здесь:

DROP PROCEDURE throws syntax error

SQL-сервер будет жаловаться на использование:

Restore Database @dbName 

Потому что в настоящее время передается как буквальная строка. Вам нужно будет написать что-то вроде этого, чтобы выполнить свой код.

declare @sql varchar(64); 

set @sql = 'RESTORE DATABASE ' + @dbName; 

exec(@sql); 

Update
Согласно комментарий Скотта, используя:

set @sql = 'RESTORE DATABASE ' + QUOTENAME(@dbName); 

избежит строку, чтобы создать правильный SQL Server с разделителями идентификатор:

https://msdn.microsoft.com/en-us/library/ms176114.aspx

+2

Обратите внимание, что вы действительно можете сделать 'set @sql = 'RESTORE DATABASE' + QUOTENAME (@dbName);' [QUOTENAME'] (https://msdn.microsoft.com/en-us/library/ms176114 ,aspx), убедитесь, что имя базы данных имеет правильный '[]' вокруг нее, так что если вы получите базу данных с пробелом в имени, она будет корректно восстановлена. –

+0

Спасибо, я не слышал/использовал эту функцию до –

1

@dbName является недействительным.

При передаче параметра вы должны соблюдать правила того, где переменные разрешены для использования в синтаксисе sql. При использовании инструкции SELECT вы можете использовать переменную в определении столбца или where, но не может использоваться для ссылки на конкретный объект базы данных. То же самое справедливо и для ALTER DATABASE, ATLER TABLE, CREATE TABLE и т. Д.

Так что в принципе параметр не может иметь место имени объекта, если вы не используете динамический sql, такой как построение строки sql из параметров как предполагает другой ответ.Динамический SQL таким образом может быть очень открытым для атак SQL Injection, поэтому вы должны убедиться, что ваш источник (ы) доверен.

Наконец, вы спрашиваете, возможно ли это, и ответ да, очень возможно запустить команды резервного копирования/изменения базы данных и т. Д. Из C#, если у вас есть разрешения, а ваш SQL-оператор корректен, он будет выполнен.

1

Вы не можете параметризовать все в SQL.

SELECT * FROM myTable WHERE customerID = @custID 

действителен.

SELECT * FROM @tablename 

нет. Ни один из них не использует ALTER DATABASE или RESTORE DATABASE с параметром.

Это по дизайну. Исправление состоит в том, чтобы выполнить замену в строке перед отправкой ее на SQL, но тогда, конечно, вам придется очистить входящие параметры для плохих вещей.

+1

. Вам не нужно счищать, SQL предоставляет функцию, позволяющую избежать имени, чтобы он создавал соответствующий объект с помощью ['QUOTENAME'] (https://msdn.microsoft .com/en-us/library/ms176114.aspx), поэтому, если вы хотите иметь базу данных с именем 'This Is [Weird]; drop database foo; 'он будет включен в имя' [This Is [Weird]]; drop database foo;] 'который [является допустимым именем базы данных] (http://i.stack.imgur.com/opZYS.png) –