Conflicting assembly names after copying script components in SSIS
Asked Answered
W

3

7

I created an SSIS package (in VS 2013) with a data flow task containing several script components. I needed to add another data flow task that is similar to the existing one so I copied and pasted it into the package and added a precedence constraint so that the new one runs after the old one. I changed the code in the script components in the new data flow task according to business requirements and ran the package. I soon realized that when the new data flow task runs, the script components from the old data flow were being executed. Weird.

After some internet searching, I found this page:

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

which describes how the copying of the data flow task doesn't change the assembly name or root namespace in any script component in the new data flow. Apparently, at runtime the assembly of the new script is overwritten by that of the old script. The web page says the problem can be fixed by copying the script component to another package and then copying it back to the original package. Presumably that will change the assembly name of each script component. I'm glad it worked for him but it didn't work for me.

I also tried changing the assembly name in the script editor here:

Application properties

But that didn't work either. The code in the old script component still runs in the new data flow. I'm guessing the root namespace needs to change but that's greyed out so I can't manually change it. So I'm still looking for a solution that doesn't involve recreating the script components from scratch since they contain multiple outputs with many columns in each. Hoping you might have an answer.

Wellfixed answered 8/7, 2016 at 20:0 Comment(1)
I don't know that it is easier than recreating the component but I found you can just rename the assembly in the DTSX file through navigating the xml.Peel
P
3

I have been curious about your question because I have had this happen to me in the past and I corrected by simply recreating the script component and copying relevant code. Obviously less than idea but.....

anyway, I confirmed by looking at the dtsx package in notepad (note it is just xml) that the issue with copying is the application/namespace remains the same so when compiled one just overwrites the other.

I found a good blog on a way to do it in a Sript Task (http://agilebi.com/jwelch/2008/04/30/renaming-the-script-project-in-ssis-2008/) but the same solution doesn't look like it is available in a Script Component because the later does not have expressions available to it for use.....

Script Component Method - Successfully Tested

However, I did just recreated the problem and discovered a solution. Thought admittedly I am not sure if my solution would be easier than recreating the script component and copying in the script. Anyway, a DTSX is an XML file so you can edit it directly! In my case and likely standard there was 10 locations that I had to change the values. So

  • find the value of the namespace/assembly name (several places I will let you figure out where you want to get it from).
  • Then change the file extension on your package to XML and open in an XML editor such as visual studio
  • Do a find all to find all of the locations the assembly name appears, remember it will be twice the amount you need to change because both components are using the same name.
  • When navigating the results just a few lines above the first result will be a name identifying which component and data flow you are in.
  • step through the results until you find the component you want to change, again in my case that was the 11th result. Then change all the values between that result and the end of the search or until you find you are in another data flow/component.
  • rename the file back to .dtsx and you are done.

Create a backup copy of your package just in case!

Script Task Method - Successfully Tested

And the easy method if you are discussing a Script task in the Control Flow.

  • go to properties of Script task and choose Expressions
  • The input a new value in the property of ScriptProjectName
  • confirm your change has taken place by choosing edit to edit the script.

enter image description here

Peel answered 8/7, 2016 at 23:19 Comment(0)
W
0

Thanks Matt. I had the same idea. But I was trying to avoid touching the XML since it can get tricky with some assembly names needing to change and others remaining as is. Also, my data flow currently has 7 script components that all may need their assembly names changed so again it's painful to change the XML file. I believe I've solved the issue by taking each data flow task and moving each into its own subpackage. SSIS doesn't appear to confuse the separated data flows during execution, even with the same assembly/namespaces. Let me know if you think that won't work.

Wellfixed answered 11/7, 2016 at 16:38 Comment(1)
I just hacked the XML manually using VS code with a new guid. It's not as scary as it seems. The assembly name is in blocks of 18 occurrences, easy enough to separate the 2 using VS code's find and replace. Hope this helps somebody.Petiolate
T
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----");
            }
        }
Tambac answered 23/9, 2019 at 15:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.