6

我创建了一个 SSIS 包(在 VS 2013 中),其中包含一个包含多个脚本组件的数据流任务。我需要添加另一个与现有任务类似的数据流任务,因此我将其复制并粘贴到包中并添加了优先约束,以便新任务在旧任务之后运行。我根据业务需求修改了新建数据流任务中脚本组件中的代码并运行包。我很快意识到,当新的数据流任务运行时,旧数据流中的脚本组件正在执行。诡异的。

经过一些互联网搜索,我找到了这个页面:

http://kohera.be/blog/sql-server/the-dangers-of-copying-script-components-in-ssis/

它描述了数据流任务的复制如何不更改新数据流中任何脚本组件中的程序集名称或根命名空间。显然,在运行时,新脚本的程序集被旧脚本的程序集覆盖。该网页说可以通过将脚本组件复制到另一个包然后将其复制回原始包来解决此问题。大概这会改变每个脚本组件的程序集名称。我很高兴这对他有用,但对我没有用。

我还尝试在此处的脚本编辑器中更改程序集名称:

应用程序属性

但这也没有用。旧脚本组件中的代码仍然在新数据流中运行。我猜根命名空间需要更改,但它是灰色的,所以我无法手动更改它。所以我仍在寻找一种不涉及从头开始重新创建脚本组件的解决方案,因为它们包含多个输出,每个输出都有很多列。希望你能有答案。

4

3 回答 3

2

我一直对您的问题感到好奇,因为我过去曾发生过这种情况,我通过简单地重新创建脚本组件并复制相关代码来纠正。显然不是想法,但......

无论如何,我通过查看记事本中的 dtsx 包(注意它只是 xml)确认,复制的问题是应用程序/命名空间保持不变,因此在编译时一个只会覆盖另一个。

我在Sript Taskhttp://agilebi.com/jwelch/2008/04/30/renaming-the-script-project-in-ssis-2008/)中找到了一个很好的博客,但同样的解决方案没有'看起来它在 a中不可用,Script Component因为后者没有可供使用的表达式.....

脚本组件方法 - 成功测试

但是,我确实只是重新创建了问题并找到了解决方案。诚然,我不确定我的解决方案是否比重新创建脚本组件和复制脚本更容易。无论如何,DTSX 是一个 XML 文件,因此您可以直接对其进行编辑!在我的情况和可能的标准中,我必须更改 10 个位置的值。所以

  • 找到命名空间/程序集名称的值(我会让你找出你想从哪里得到它的几个地方)。
  • 然后将包上的文件扩展名更改为 XML 并在 XML 编辑器(如 Visual Studio)中打开
  • 查找所有以查找出现装配名称的所有位置,记住这将是您需要更改的数量的两倍,因为两个组件都使用相同的名称。
  • 导航结果时,仅在第一个结果上方几行将是一个名称,用于标识您所在的组件和数据流。
  • 逐步检查结果,直到找到要更改的组件,在我的情况下也是第 11 个结果。然后更改该结果和搜索结束之间的所有值,或者直到您发现您在另一个数据流/组件中。
  • 将文件重命名为 .dtsx 就完成了。

为您的包裹创建一个备份副本以防万一!

脚本任务方法 - 成功测试

如果您Script task在控制流中讨论,那么简单的方法。

  • propertiesofScript task并选择Expressions
  • 在属性中输入一个新值ScriptProjectName
  • 通过选择编辑来编辑脚本来确认您的更改已经发生。

在此处输入图像描述

于 2016-07-08T23:19:46.610 回答
0

The "copy from another VS solution" workaround doesn't always work, at least not for me.

I created a little C# routine that brute-force repairs the .dtsx XML by performing some basic text processing. The Script component assembly definitions are found in groups, with each one starting and ending with grep-able RegEx patterns. The general idea is to iterate over the lines of the file, and: 1. When you see the starting RegEx Pattern, parse out the Assembly ID and generate a new Assembly ID ("SC_" followed by a 32-char GUID) 2. Until you find the ending RegEx Pattern, replace the old Assembly ID with the new Assembly ID

NOTE: There are placeholders, but no support, for SQL Server versions other than 2012.

As with everything, use this code at your own risk. And feel free to let me know if you see any errors:

        /// <summary>Repair a .dtsx file with conflicting AssemblyIDs in Script copmonents</summary>
        /// <remarks>
        /// A simple text-processing routine to repair the result of the Visual Studio bug that causes conflicting assembly names to 
        /// appear when copying a Script component into an SSIS Data Flow container.
        /// 
        /// Input: A corrupted .dtsx file containing an SSIS package with Script components having duplicate Assembly IDs
        /// Output: A repaired .dtsx file with each Script component having a unique Assembly ID
        /// </remarks>
        /// <param name="inputDtsxFile">The full path to the .dtsx package to repair</param>
        /// <param name="outputDtsxFile">The full path name of the repaired .dtsx package.  Optional -  
        /// Null or default results in a file in the same folder as the source file, with "_repairedNNNN" appended to the file name, incrementing NNNN by 1 each time.</param>
        /// <param name="startRegEx">Optional - Overrides the default RegEx for the version of SQL Server found in parameter targetVersion</param>
        /// <param name="endRegEx">Optional - Overrides the default RegEx for the version of SQL Server found in parameter targetVersion</param>
        /// <param name="targetVersion">Optional - The version of SQL Server the package build target is for.  Default (and only version currently supported) is "SQL Server 2016"</param>
        private void RepairDtsxScriptComponentCopyError(string inputDtsxFile, string outputDtsxFile = null, string targetVersion = null, string startRegEx = null, string endRegEx = null)
        {
            //Default the target version to "SQL Server 2016"
            if (targetVersion == null)
                targetVersion = "SQL Server 2016";

            //Make sure if start or end RegEx patters are supplied, BOTH are supplied
            if (startRegEx != null || endRegEx != null)
            {
                if (startRegEx == null)
                {
                    Console.WriteLine("If either start or end regex is specified, both must be specified");
                    return;
                }
            }

            //Set any variables specific to a target version of Visual Studio for SSIS
            switch (targetVersion)
            {
                case "SQL Server 2012":
                    Console.WriteLine("SQL Server 2012 target version not supported yet.");
                    return;
                case "SQL Server 2014":
                    Console.WriteLine("SQL Server 2014 target version not supported yet.");
                    return;
                case "SQL Server 2016":
                    startRegEx = "\\[assembly: AssemblyTitle\\(\"SC_[a-zA-Z0-9]{32}\"\\)\\]";
                    endRegEx = "typeConverter=\"NOTBROWSABLE\"";
                    break;
                case "SQL Server 2018":
                    Console.WriteLine("SQL Server 2018 target version not supported yet.");
                    return;
            }

            try
            {
                //Variables for output stream:
                string folderName = "";
                string fileName = "";
                string fileExt = "";

                //If no output file name is supplied, use the folder where the input file is located, 
                //  look for files with the same name as the input file plus suffix "_repairedNNNN"
                //  and increment NNNN by one to make the new file name
                //  e.g. fixme.dtsx --> fixme_repared0000.dtsx   (the first time it's cleaned)
                //       fixme.dtsx --> fixme_repared0001.dtsx   (the second time it's cleaned)
                //  and so on.
                if (outputDtsxFile == null || String.IsNullOrEmpty(outputDtsxFile) || String.IsNullOrWhiteSpace(outputDtsxFile))
                {
                    folderName = Path.GetDirectoryName(inputDtsxFile);
                    fileName = Path.GetFileNameWithoutExtension(inputDtsxFile) + "_repaired";
                    fileExt = Path.GetExtension(inputDtsxFile);

                    int maxserial = 0;

                    //Output file will be in the form originalname_NNNN.dtsx
                    //Each run of the program will increment NNNN

                    //First, find the highest value of NNNN in all the file names in the target folder:
                    foreach (string foundFile in Directory.GetFiles(folderName, fileName + "_*" + fileExt))
                    {
                        string numStr = Regex.Replace(Path.GetFileNameWithoutExtension(foundFile), "^.*_", "");
                        int fileNum = -1;
                        if (int.TryParse(numStr, out fileNum))
                            maxserial = Math.Max(maxserial, fileNum);
                    }
                    //Increment by 1
                    maxserial++;

                    //Create new file name
                    fileName = Path.Combine(folderName, fileName + "_" + maxserial.ToString("0000") + fileExt);
                }
                else //Use the value passed in as a parameter
                    fileName = outputDtsxFile;


                //Create the new StreamWriter handle for the output file
                Stream outputStream = File.OpenWrite(fileName);
                StreamWriter outputWriter = new StreamWriter(outputStream);

                Console.WriteLine("----START----");

                //Open the input file
                StreamReader inputFile = new StreamReader(inputDtsxFile);

                //Set up some variables
                string line = "";
                int linepos = 1;
                int matchcount = 1;
                int assyCount = 0;
                string assyname = "";
                string oldGuidLC = "";
                string oldGuidUC = "";
                string newGuidLC = "";
                string newGuidUC = "";
                Boolean inAssembly = false;
                while ((line = inputFile.ReadLine()) != null)
                {
                    //Look for the start of a section that contains the assembly name:
                    if (!inAssembly && Regex.IsMatch(line, startRegEx))
                    {

                        //Get the new GUID
                        assyname = Regex.Match(line, "SC_[a-zA-Z0-9]{32}").ToString();
                        oldGuidLC = assyname;
                        oldGuidUC = "SC_" + assyname.Substring(3, 32).ToUpper();
                        newGuidLC = "SC_" + Guid.NewGuid().ToString().Replace("-", "");
                        newGuidUC = newGuidLC.ToUpper();

                        //Set the "in Assembly" flag
                        inAssembly = true;

                        Console.WriteLine("Found Assembly " + assyname + " at line " + linepos.ToString());
                        Console.WriteLine("Old GUID (LC): " + oldGuidLC);
                        Console.WriteLine("Old GUID (UC): " + oldGuidUC);
                        Console.WriteLine("New GUID (LC): " + newGuidLC);
                        Console.WriteLine("New GUID (UC): " + newGuidUC);
                        assyCount++;
                    }

                    //Substitute the old GUID for the new GUID, but only bother doing it when in an assembly section
                    if (inAssembly && Regex.IsMatch(line, "SC_[a-zA-Z0-9]{32}"))
                    {
                        line = line.Replace(oldGuidLC, newGuidLC);
                        line = line.Replace(oldGuidUC, newGuidUC);
                        Console.WriteLine(linepos.ToString("000000") + "/" + assyCount.ToString("0000") + "/" + matchcount++.ToString("0000") + "/" + assyname + ": " + line);
                    }
                    //Look for the end of the assembly section
                    if (inAssembly && Regex.IsMatch(line, endRegEx) && Regex.IsMatch(line, "SC_[a-zA-Z0-9]{32}"))
                    {
                        inAssembly = false;
                    }
                    //Output the line
                    outputWriter.WriteLine(line);
                    linepos++;
                }
                inputFile.Close();
                outputWriter.Close();
                outputStream.Close();
                Console.WriteLine("----DONE----");
            }
            catch (Exception ex)
            {
                Console.WriteLine("ERROR: " + ex.Message);
                Console.WriteLine("----DONE----");
            }
        }
于 2019-09-23T15:09:52.060 回答
0

谢谢马特。我也有同样的想法。但我试图避免接触 XML,因为它可能会因为一些程序集名称需要更改而其他程序集名称保持原样而变得棘手。此外,我的数据流目前有 7 个脚本组件,它们都可能需要更改它们的程序集名称,因此再次更改 XML 文件很痛苦。我相信我已经通过处理每个数据流任务并将每个任务移动到自己的子包中来解决了这个问题。SSIS 在执行期间似乎不会混淆分离的数据流,即使使用相同的程序集/命名空间也是如此。如果你认为这行不通,请告诉我。

于 2016-07-11T16:38:01.743 回答