Использование SMO для копирования базы данных и данных
Я пытаюсь сделать копию базы данных для новой базы данных на том же сервере. Сервер - это мой локальный компьютер с 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();
}
}
}
Что я здесь делаю, это сначала создать новую базу данных, затем сгенерировать копию script с помощью класса Transfer
и, наконец, запустить script в новой базе данных.
Это отлично подходит для копирования структуры, но параметр CopyData
не работает!
Существуют ли какие-либо недокументированные лимиты для опции CopyData
? В документации только говорится, что параметр указывает, будут ли данные скопированы.
Я попытался использовать метод TransferData()
для копирования базы данных без использования script, но затем я получаю исключение, в котором говорится: "Не удалось подключиться к серверу" с внутренним исключением, которое говорит "Связанный с сетью или экземпляр- произошла ошибка при установлении соединения с SQL Server. Сервер не был найден или недоступен. Убедитесь, что имя экземпляра верное и что SQL Server настроен на разрешение удаленных подключений. (поставщик: поставщик именованных труб, ошибка: 40 - Не удалось открыть соединение с SQL Server)"
Я также попытался включить Named Pipes на сервере, но это не помогает.
Изменить:
Я нашел решение, которое работает, создав резервную копию, а затем восстановив ее в новой базе данных. Это довольно неуклюже, хотя и медленнее, чем должно быть, поэтому я все еще ищу лучшее решение.
Ответы
Ответ 1
Хорошо, после того, как я связался с Microsft Support, я получил правильную работу, но он медленный и более или менее бесполезный. Выполнение резервного копирования, а затем восстановление происходит намного быстрее, и я буду использовать его, пока новая копия должна жить на том же сервере, что и оригинал.
Рабочий код выглядит следующим образом:
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. Это должно быть установлено, даже если цель та же, что и источник. Кроме того, мне пришлось подключаться к серверу как именованный экземпляр вместо использования других параметров подключения.
Ответ 2
У меня было желание получить эту работу и придумал ответ, который не использует класс 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();
}
}
Ответ 3
Попробуйте установить SetDefaultInitFields в true на Сервер.
> У меня была такая же проблема, что и объект базы данных SMO. Я полагаю, это связано с тем, что сервер sql не любит извлекать целые объекты и коллекции сразу, вместо этого ленивая загрузка всего, что вызывает обратную связь для каждого поля, что для всей базы данных довольно неэффективно.
Ответ 4
Вот мое решение:
3.1. Вы можете использовать команду в инструменте запросов: EXEC OldDatabase.dbo.sp_helpfile;
для определения пути OldDatabase хранится в случае, если вы хотите сохранить NewDatabase в той же папке.
или Вы можете сохранить NewDatabase в новом пути, который вы хотите
-
используйте эту команду в инструменте запросов
ВОССТАНОВИТЬ БАЗА ДАННЫХ NewDatabase FROM DISK = 'E:\databackup\Old.bak'
С MOVE 'OldDatabase' TO 'E:\Новый путь (или тот же путь)\NewDatabase_Data.mdf',
MOVE 'OldDatabase_log' TO 'E:\Новый путь (или тот же путь)\NewDatabase_Log.ldf';
Примечание. Вы можете использовать эту команду в С# с помощью: Создать процедуру Store в sql, которая включает команды Above. И вы можете вызвать процедуру хранилища в С#