【问题标题】:Using SMO to copy a database and data使用 SMO 复制数据库和数据
【发布时间】:2010-09-21 01:39:11
【问题描述】:

我正在尝试将数据库复制到同一服务器上的新数据库中。服务器是我在 Windows XP 下运行 SQL 2008 Express 的本地计算机。 使用 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 的连接)"

我也尝试在服务器上启用命名管道,但这无济于事。

编辑: 我找到了一个解决方案,通过备份然后将其恢复到新数据库来工作。虽然它很笨拙,而且比它应该的要慢,所以我仍在寻找更好的解决方案。

【问题讨论】:

    标签: c# .net sql-server-2008 smo


    【解决方案1】:

    好吧,在联系了 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 属性。即使目标与源相同,也必须设置此项。此外,我必须以命名实例的形式连接到服务器,而不是使用其他连接选项。

    【讨论】:

    • 备份和恢复总是会更快,因为它不受锁定开销、事务等的影响。备份的设计速度非常快。对不起,我没有早点找到你! :-D
    • 但是运行备份可能会干扰当前的备份顺序和日志传输/截断。您可以使用 SMO 创建一个 COPY_ONLY 备份,如msdn.microsoft.com/en-us/library/ms191495.aspx 所述“
    • 另外,使用 C# SMO 进行备份和恢复会丢失所有用户和登录信息,不是吗?
    • 值得注意的是,这仅在您以“sa”用户身份登录时才有效,否则当您创建数据时,您的用户被分配为 dbo,然后它尝试在目标数据库中创建一个新用户你当前的用户,你得到一个例外。即使你明确告诉它不要复制登录名、用户名或出于某种原因它想要创建当前用户的任何内容。
    • 我使用了上面的代码,但是当我设置属性 transfer.copydata=true 时,我在“对象引用未设置为对象的实例”中遇到异常。而且没有这个属性意味着,Schema被复制没问题..如何解决这个问题
    【解决方案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();
           }
       }
    

    【讨论】:

    • 在 sb.append(line+"\r\n") 上出现错误,“对象引用未设置为对象的实例”
    • @franchescototti:不,你没有。除非您对导致 sb 为空的代码进行了修改,否则您不会收到该错误。
    【解决方案3】:

    尝试在 Server 对象上将 SetDefaultInitFields 设置为 true。

    SMO 数据库对象运行缓慢时遇到了同样的问题。我猜这是因为 sql server 不喜欢一次检索整个对象和集合,而是延迟加载所有内容,导致每个字段往返,这对于整个数据库来说效率非常低。

    【讨论】:

      【解决方案4】:

      这是我的解决方案:

      1. 我有一个名为 Olddatabase 的数据库
      2. 我把它备份到 E:\databackup\Old.bak

      3. 如果您想在同一台服务器上创建一个名为 NewDatabase 的 Olddatabase 重复数据库

      3.1 您可以在查询工具中使用命令:EXEC OldDatabase.dbo.sp_helpfile; 如果您想将 NewDatabase 保存在同一文件夹中,则确定 OldDatabase 的路径。

      或者您可以将 NewDatabase 保存在您想要的新路径中

      1. 在查询工具中使用该命令

        从磁盘恢复数据库 NewDatabase = 'E:\databackup\Old.bak' WITH MOVE 'OldDatabase' TO 'E:\New path (or the same path)\NewDatabase_Data.mdf', MOVE 'OldDatabase_log' TO 'E:\New 路径(或相同路径)\NewDatabase_Log.ldf';

      注意:您可以在 c# 中使用上述命令:在 sql 中创建一个包含上述命令的存储过程。您可以在 C# 中调用存储过程

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多