c# - 为什么 SMO 脚本程序不生成外键 header ?

标签 c# sql-server-2008 tsql smo

我正在创建一个程序来使用随 SQL Server 2008 分发的 SMO 库生成我的数据库模式。

我得到了脚本输出代码,当它配置为输出所有内容时,它实际上与 SQL Server Management Studio 输出相同,但有一个奇怪的异常(exception):它不输出它在以下位置生成的外键约束的注释 header 底部,而 SSMS 是。谁能弄清楚这是为什么?这是我的代码:

private void btnExportScript_Click(object sender, EventArgs ea) {
    Server srv = setupConnection();

    // Reference the database
    if (!srv.Databases.Contains(cbChooseDb.SelectedItem.ToString())) {
        _utils.ShowError("Couldn't find DB '" + cbChooseDb.SelectedItem.ToString() + "'.");
        return;
    }
    Database db = srv.Databases[cbChooseDb.SelectedItem.ToString()];

    StringBuilder builder = new StringBuilder();
    try {
        Scripter scrp = new Scripter(srv);
        scrp.Options.AppendToFile = false;
        scrp.Options.ToFileOnly = false;
        scrp.Options.ScriptDrops = false;             // Don't script DROPs
        scrp.Options.Indexes = true;                  // Include indexes
        scrp.Options.DriAllConstraints = true;        // Include referential constraints in the script
        scrp.Options.Triggers = true;                 // Include triggers
        scrp.Options.FullTextIndexes = true;          // Include full text indexes
        scrp.Options.NonClusteredIndexes = true;      // Include non-clustered indexes
        scrp.Options.NoCollation = false;             // Include collation
        scrp.Options.Bindings = true;                 // Include bindings
        scrp.Options.SchemaQualify = true;            // Include schema qualification, eg. [dbo]
        scrp.Options.IncludeDatabaseContext = false;
        scrp.Options.AnsiPadding = true;
        scrp.Options.FullTextStopLists = true;
        scrp.Options.IncludeIfNotExists = false;
        scrp.Options.ScriptBatchTerminator = true;
        scrp.Options.ExtendedProperties = true;
        scrp.Options.ClusteredIndexes = true;
        scrp.Options.FullTextCatalogs = true;
        scrp.Options.SchemaQualifyForeignKeysReferences = true;
        scrp.Options.XmlIndexes = true;
        scrp.Options.IncludeHeaders = true;

        // Prefectching may speed things up
        scrp.PrefetchObjects = true;

        var urns = new List<Urn>();

        // Iterate through the tables in database and script each one.
        foreach (Table tb in db.Tables) {
            if (tb.IsSystemObject == false) {
                // Table is not a system object, so add it.
                urns.Add(tb.Urn);
            }
        }

        // Iterate through the views in database and script each one.  Display the script.
        foreach (Microsoft.SqlServer.Management.Smo.View view in db.Views) {
            if (view.IsSystemObject == false) {
                // View is not a system object, so add it.
                urns.Add(view.Urn);
            }
        }

        // Iterate through the stored procedures in database and script each one.  Display the script.
        foreach (StoredProcedure sp in db.StoredProcedures) {
            if (sp.IsSystemObject == false) {
                // Procedure is not a system object, so add it.
                urns.Add(sp.Urn);
            }
        }

        // Start by manually adding DB context
        builder.AppendLine("USE [" + db.Name + "]");
        builder.AppendLine("GO");

        System.Collections.Specialized.StringCollection sc = scrp.Script(urns.ToArray());
        foreach (string st in sc) {
            // It seems each string is a sensible batch, and putting GO after it makes it work in tools like SSMS.
            // Wrapping each string in an 'exec' statement would work better if using SqlCommand to run the script.
            builder.Append(st.Trim(new char[] { '\r', '\n' }) + "\r\nGO\r\n");
        }
    }
    catch (Exception ex) {
        showExceptionError("Couldn't generate script.", ex);
        return;
    }

    try {
        File.WriteAllText(txtExportToFile.Text, builder.ToString());
        _utils.ShowInfo("DB exported to script at: " + txtExportToFile.Text);
    }
    catch (Exception ex) {
        showExceptionError("Couldn't save script file.", ex);
        return;
    }
}

请注意,外键属于 DRI 约束类别,并且由于 scrp.Options.DriAllConstraints = true; 而被编写脚本。

最佳答案

我这里有一个解决方案:Can't get EnumScript() to generate constraints

出于某种原因,Scripter 在给定一个 Urn 列表时不会发出 DRI 约束(外键等),但如果一次给定一个 Urn,它就会发出。这里的诀窍是按照它们的祖先顺序给出 Urns:表必须在它们被约束引用之前定义。为此,我使用了 DependencyWalker

这是一个概要:

        var urns = new List<Urn>();
        Scripter schemaScripter = new Scripter(srv) { Options = schemaOptions };
        Scripter insertScripter = new Scripter(srv) { Options = insertOptions };
        var dw = new DependencyWalker(srv);

        foreach (Table t in db.Tables)
            if (t.IsSystemObject == false)
                urns.Add(t.Urn);
        DependencyTree dTree = dw.DiscoverDependencies(urns.ToArray(), true);
        DependencyCollection dColl = dw.WalkDependencies(dTree);

        foreach (var d in dColl)
        {
            foreach (var s in schemaScripter.Script(new Urn[] { d.Urn }))
                strings.Add(s);
            strings.Add("GO");
            if (scriptData)
            {
                int n = 0;
                foreach (var i in insertScripter.EnumScript(new Urn[] {d.Urn}))
                {
                    strings.Add(i);
                    if ((++n) % 100 == 0)
                        strings.Add("GO");
                }
            }
        }

注意:每隔一段时间添加一个“GO”可以使批处理大小保持较小,这样 SSMS 就不会耗尽内存。

关于c# - 为什么 SMO 脚本程序不生成外键 header ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13330973/

相关文章:

sql-server - "Prevent saving changes that require the table to be re-created"负面影响

sql - 如何找到子字符串并替换它?

c# - 无法更新 sql server 中的 varbinary(max) 列

performance - 使用 IN 运算符的 SQL 查询停止寻找索引并开始扫描它,速度慢了 100 倍

sql-server - TSQL 检索当月/当年的所有记录

sql - 如何对 GROUP BY 中的多个条件的列求和

c# - asp.net c# 中的日历控件

c# - 通过 wcf 序列化谓词

c# - 如何使用Container获取绑定(bind)数据?

c# - 启动进程时出现错误 C0000142