Использование SMO для копирования базы данных и данных - PullRequest
22 голосов
/ 06 ноября 2008

Я пытаюсь скопировать базу данных в новую базу данных на том же сервере. Сервер - это мой локальный компьютер под управлением SQL 2008 Express под Windows XP. Это легко сделать с помощью класса SMO.Transfer, и он почти работает!

Мой код выглядит следующим образом (несколько упрощенно):

Server server = new Server("server");
Database sourceDatabase = server.Databases["source database"];

Database newDatbase = new Database(server, "new name");
newDatbase.Create();

Transfer transfer = new Transfer(sourceDatabase);
transfer.CopyAllObjects = true;
transfer.Options.WithDependencies = true;
transfer.DestinationDatabase = newDatbase.Name;
transfer.CopySchema = true;
transfer.CopyData = true;
StringCollection transferScript = transfer.ScriptTransfer();

using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    using (SqlCommand switchDatabase = new SqlCommand("USE " + newDatbase.Name, conn))
    {
        switchDatabase.ExecuteNonQuery();
    }

    foreach (string scriptLine in transferScript)
    {
        using (SqlCommand scriptCmd = new SqlCommand(scriptLine, conn, transaction))
        {
            int res = scriptCmd.ExecuteNonQuery();
        }
    }
}

Здесь я сначала создаю новую базу данных, затем создаю сценарий копирования с использованием класса Transfer и, наконец, запускаю сценарий в новой базе данных.

Это прекрасно работает для копирования структуры, но опция CopyData не работает!

Есть ли недокументированные ограничения для опции CopyData? В документации только сказано, что опция указывает, копируются ли данные.

Я попытался с помощью метода TransferData() скопировать базу данных без использования сценария, но затем я получил исключение, которое говорит: «Не удалось подключиться к серверу», с внутренним исключением, которое говорит: «Ошибка, связанная с сетью или экземпляром» произошла при установлении соединения с SQL Server. Сервер не был найден или был недоступен. Убедитесь, что имя экземпляра указано правильно и что SQL Server настроен для разрешения удаленных подключений. (Поставщик: поставщик именованных каналов, ошибка: 40 - не удалось открыть соединение с SQL Server) "

Я также пытался включить именованные каналы на сервере, но это не помогает.

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

Ответы [ 4 ]

16 голосов
/ 26 ноября 2008

Что ж, после обращения в службу поддержки Microsft я все заработал, но он медленный и более или менее бесполезный. Резервное копирование, а затем восстановление выполняется намного быстрее, и я буду использовать его, если новая копия будет находиться на том же сервере, что и оригинал.

Рабочий код выглядит следующим образом:

ServerConnection conn = new ServerConnection("rune\\sql2008");
Server server = new Server(conn);

Database newdb = new Database(server, "new database");
newdb.Create();

Transfer transfer = new Transfer(server.Databases["source database"]);
transfer.CopyAllObjects = true;
transfer.CopyAllUsers = true;
transfer.Options.WithDependencies = true;
transfer.DestinationDatabase = newdb.Name;
transfer.DestinationServer = server.Name;
transfer.DestinationLoginSecure = true;
transfer.CopySchema = true;
transfer.CopyData = true;
transfer.Options.ContinueScriptingOnError = true;
transfer.TransferData();

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

3 голосов
/ 18 октября 2011

Я попытался заставить это работать и придумал ответ, в котором не используется класс Transfer. Вот метод, который я использовал:

       public bool CreateScript(string oldDatabase, string newDatabase)
   {
       SqlConnection conn = new SqlConnection("Data Source=.;Initial Catalog=" + newDatabase + ";User Id=sa;Password=sa;");
       try
       {
           Server sv = new Server();
           Database db = sv.Databases[oldDatabase];

           Database newDatbase = new Database(sv, newDatabase);
           newDatbase.Create(); 

           ScriptingOptions options = new ScriptingOptions();
           StringBuilder sb = new StringBuilder();
           options.ScriptData = true;
           options.ScriptDrops = false;
           options.ScriptSchema = true;
           options.EnforceScriptingOptions = true;
           options.Indexes = true;
           options.IncludeHeaders = true;
           options.WithDependencies = true;

           TableCollection tables = db.Tables;

           conn.Open();
           foreach (Table mytable in tables)
           {
               foreach (string line in db.Tables[mytable.Name].EnumScript(options))
               {
                   sb.Append(line + "\r\n");
               }
           }
           string[] splitter = new string[] { "\r\nGO\r\n" };
           string[] commandTexts = sb.ToString().Split(splitter, StringSplitOptions.RemoveEmptyEntries);
           foreach (string command in commandTexts)
           {
               SqlCommand comm = new SqlCommand(command, conn);
               comm.ExecuteNonQuery();
           }
           return true;
       }
       catch (Exception e)
       {
           System.Diagnostics.Debug.WriteLine("PROGRAM FAILED: " + e.Message);
           return false;
       }
       finally
       {
           conn.Close();
       }
   }
2 голосов
/ 23 июня 2009

Попробуйте установить SetDefaultInitFields в значение true на Сервер объект.

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

1 голос
/ 08 декабря 2013

Вот мое решение:

  1. У меня есть база данных с именем Olddatabase
  2. Я создаю резервную копию на E: \ databackup \ Old.bak

  3. Если вы хотите создать Дубликат базы данных из Olddatabase на том же сервере с именем NewDatabase

3.1 Вы можете использовать команду в инструменте запроса: EXEC OldDatabase.dbo.sp_helpfile; Чтобы определить путь OldDatabase хранится в случае, если вы хотите сохранить NewDatabase в той же папке.

или Вы можете сохранить NewDatabase в новом Путь, который вы хотите

  1. используйте эту команду в инструменте Query

    ВОССТАНОВИТЬ БАЗУ ДАННЫХ NewDatabase FROM DISK = 'E: \ databackup \ Old.bak' WITH MOVE 'OldDatabase' TO 'E: \ Новый путь (или тот же путь) \ NewDatabase_Data.mdf', MOVE 'OldDatabase_log' TO 'E: \ Новый путь (или тот же путь) \ NewDatabase_Log.ldf';

Примечание: вы можете использовать эти команды выше в c # с: Создать процедуру сохранения в sql, которая включает команды Above. И вы можете вызвать процедуру магазина в C #

...