Противоречивые имена сборок после копирования компонентов скрипта в SSIS

Я создал пакет служб SSIS (в VS 2013) с задачей потока данных, содержащей несколько компонентов сценария. Мне нужно было добавить еще одну задачу потока данных, которая похожа на существующую, поэтому я скопировал и вставил ее в пакет и добавил ограничение приоритета, чтобы новая выполнялась после старой. Я изменил код в компонентах сценария в новой задаче потока данных в соответствии с бизнес-требованиями и запустил пакет. Вскоре я понял, что при запуске новой задачи потока данных выполняются компоненты сценария из старого потока данных. Weird.

После некоторых поисков в интернете я нашел эту страницу:

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

который описывает, как копирование задачи потока данных не изменяет имя сборки или корневое пространство имен в любом компоненте сценария в новом потоке данных. Очевидно, что во время выполнения сборка нового скрипта перезаписывается сборкой старого скрипта. На веб-странице сказано, что проблему можно решить, скопировав компонент сценария в другой пакет, а затем скопировав его обратно в исходный пакет. Предположительно, это изменит имя сборки каждого компонента скрипта. Я рад, что это сработало для него, но это не сработало для меня.

Я также попытался изменить имя сборки в редакторе скриптов здесь:

Свойства приложения

Но это тоже не сработало. Код в старом компоненте скрипта все еще выполняется в новом потоке данных. Я предполагаю, что корневое пространство имен должно измениться, но оно серого цвета, поэтому я не могу изменить его вручную. Поэтому я все еще ищу решение, которое не включает воссоздание компонентов скрипта с нуля, так как они содержат несколько выходных данных со многими столбцами в каждом. Надеюсь, у вас может быть ответ.

2 ответа

Мне был любопытен ваш вопрос, потому что это случалось со мной в прошлом, и я исправил это, просто воссоздав компонент скрипта и скопировав соответствующий код. Очевидно, что меньше, чем идея, но.....

во всяком случае, я подтвердил, посмотрев пакет dtsx в блокноте (обратите внимание, что это просто xml), проблема с копированием заключается в том, что пространство имен application/ остается тем же, поэтому при компиляции одно просто перезаписывает другое.

Я нашел хороший блог о том, как сделать это в Sript Task ( http://agilebi.com/jwelch/2008/04/30/renaming-the-script-project-in-ssis-2008/), но это же решение не выглядит так, как оно доступно в Script Component потому что в последнем нет доступных ему выражений для использования.....

Метод компонента Script - успешно протестирован

Тем не менее, я просто воссоздал проблему и нашел решение. Правда, я не уверен, что мое решение будет проще, чем воссоздать компонент сценария и скопировать его в сценарий. В любом случае, DTSX - это файл XML, поэтому вы можете редактировать его напрямую! В моем случае и вероятном стандарте было 10 мест, которые мне пришлось изменить значения. Так

  • найдите значение имени пространства / имени сборки (в нескольких местах я дам вам понять, откуда вы хотите его получить).
  • Затем измените расширение файла в вашем пакете на XML и откройте в редакторе XML, например в Visual Studio.
  • Найдите все, чтобы найти все местоположения, в которых отображается имя сборки, помните, что это будет вдвое больше, чем нужно изменить, поскольку оба компонента используют одно и то же имя.
  • При навигации по результатам всего на несколько строк над первым результатом будет указано имя, указывающее, в каком компоненте и в каком потоке данных вы находитесь.
  • пошагово просматривайте результаты, пока не найдете компонент, который хотите изменить, опять же в моем случае это был 11-й результат. Затем измените все значения между этим результатом и концом поиска или пока не обнаружите, что находитесь в другом потоке данных / компоненте.
  • переименуйте файл обратно в.dtsx, и все готово.

Создайте резервную копию вашего пакета на всякий случай!

Метод задания сценария - успешно протестирован

И простой метод, если вы обсуждаете Script task в потоке управления.

  • идти к properties из Script task и выбрать Expressions
  • Ввод нового значения в свойство ScriptProjectName
  • подтвердите, что ваше изменение имело место, выбрав edit для редактирования скрипта.

введите описание изображения здесь

Обходной путь "копирование из другого решения VS" не всегда работает, по крайней мере, для меня.

Я создал небольшую подпрограмму C#, которая восстанавливает файл.dtsx XML методом перебора, выполняя базовую обработку текста. Определения сборки компонентов сценария находятся в группах, каждая из которых начинается и заканчивается шаблонами регулярных выражений с поддержкой grep. Общая идея состоит в том, чтобы перебирать строки файла и: 1. Когда вы видите начальный шаблон RegEx, проанализируйте идентификатор сборки и сгенерируйте новый идентификатор сборки ("SC_", за которым следует GUID из 32 символов) 2. Пока вы не найдете конечный шаблон RegEx, замените старый идентификатор сборки новым идентификатором сборки.

ПРИМЕЧАНИЕ. Для версий SQL Server, отличных от 2012, есть заполнители, но они не поддерживаются.

Как и все, используйте этот код на свой страх и риск. И не стесняйтесь сообщить мне, если увидите какие-либо ошибки:

        /// <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----");
            }
        }

Спасибо Мэтт. У меня была такая же идея. Но я старался не трогать XML, так как он может усложниться, если некоторые имена сборок нужно изменить, а другие оставить как есть. Кроме того, в моем потоке данных в настоящее время есть 7 компонентов сценария, для которых всем, возможно, потребуется изменить имена сборок, поэтому снова болезненно менять файл XML. Я считаю, что решил проблему, взяв каждую задачу потока данных и переместив каждую в свой собственный подпакет. Похоже, службы SSIS не путают разделенные потоки данных во время выполнения даже с одинаковыми пространствами сборок / имен. Дайте мне знать, если вы думаете, что это не сработает.

Другие вопросы по тегам