Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using SMO to copy a database and data

Tags:

I am trying to make a copy of a database to a new database on the same server. The server is my local computer running SQL 2008 Express under Windows XP. Doing this should be quite easy using the SMO.Transfer class and it almost works!

My code is as follows (somewhat simplified):

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();         }     } } 

What I do here is to first create a new database, then generate a copy script using the Transfer class and finally running the script in the new database.

This works fine for copying the structure, but the CopyData option doesn't work!

Are there any undocumented limits to the CopyData option? The documentation only says that the option specifies whether data is copied.

I tried using the TransferData() method to copy the databse without using a script but then I get an exception that says "Failed to connect to server" with an inner exception that says "A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server)"

I also tried to enable Named Pipes on the server, but that doesn't help.

Edit: I found a solution that works by making a backup and then restoring it to a new database. It's quite clumsy though, and slower than it should be, so I'm still looking for a better solution.

like image 866
Rune Grimstad Avatar asked Nov 06 '08 11:11

Rune Grimstad


People also ask

Can you copy data from one database to another?

Right-click on the database name, then select "Tasks" > "Export data..." from the object explorer. The SQL Server Import/Export wizard opens; click on "Next". Provide authentication and select the source from which you want to copy the data; click "Next". Specify where to copy the data to; click on "Next".

How do I copy an existing database?

Expand Databases, right-click the desired database, point to Tasks, and then select Copy Database... If the Welcome to the Copy Database Wizard splash page appears, select Next. Select a Source Server page: Specify the server with the database to move or copy. Select the authentication method.

What is SMO database?

Applies to: SQL Server (all supported versions) Azure SQL Database Azure SQL Managed Instance Azure Synapse Analytics. SQL Server Management Objects (SMO) are objects designed for programmatic management of Microsoft SQL Server.


2 Answers

Well, after contacting Microsft Support I got it working properly, but it is slow and more or less useless. Doing a backup and then a restore is much faster and I will be using it as long as the new copy should live on the same server as the original.

The working code is as follows:

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(); 

The trick was to set the DestinationDatabase property. This must be set even if the target is that same as the source. In addition I had to connect to the server as a named instance instead of using the other connection options.

like image 166
Rune Grimstad Avatar answered Sep 27 '22 22:09

Rune Grimstad


I had a go at getting this working and have come up with an answer that doesn't use the Transfer class. Here is the Method i used:

       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();        }    } 
like image 32
emy Avatar answered Sep 27 '22 21:09

emy