Изменение строки подключения Excel Power Query в C # - PullRequest
0 голосов
/ 17 июня 2019

В файле Excel Power Query подключение для передачи данных может осуществляться с сервера SQL.У нас есть большое количество файлов, в которых указан SQL-сервер по имени, и этот сервер будет списан.Нам нужно обновить соединение, чтобы заменить старое имя сервера новым именем сервера.Это возможно, открыв файл Excel, перейдя к запросу и отредактировав имя сервера вручную.Из-за большого количества файлов желательно делать это с помощью C #.На рисунке ниже показаны поля ввода (с удаленными именами), в которых вы должны обновить это вручную.

SQL Connection Form

Сначала распакуйте файл Excel и просмотритесодержимое в папке xl > connections.xml Я бы ожидал, что там будет указано соединение, но там будет только $Workbook$

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<connections xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
  <connection id="1" keepAlive="1" name="Query" description="Connection to the query in the workbook." type="5" refreshedVersion="6" background="1" saveData="1">
    <dbPr connection="Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=&quot;table&quot;" command="SELECT * FROM [table]"/>
  </connection>
</connections>

На формах MDSN есть ссылка на эту темуа в ответе Уилла Грегга говорится:

Информация о подключении к внешнему источнику данных хранится в пакете XLSX в пользовательской части.Вы можете найти пользовательскую часть в папке customXML пакета.Например: customXml \ iem1.xml.

Содержится в item1.xml - элемент.Определение элемента можно найти в документе [MS-QDEFF]: формат файла определения запроса (https://msdn.microsoft.com/en-us/library/mt577220(v=office.12).aspx).

. Для работы с данными элемента вам необходимо будет декодировать содержимое, как описанов документе [MS-QDEFF]: Формат файла определения запроса.

После того, как данные будут декодированы, вам нужно будет проверить содержимое PackagePart. В этом пакете вы найдете информацию о подключении внешних данных вForumlas \ Section1.m part.

Это полезно, чтобы указать мне на файл item.xml в папке customXml, но не дает никаких подробностей о том, как декодировать информацию в DataMashup object. В ответе упоминается, что документ [MS-QDEFF]: Query Definition File Format доступен по этой ссылке из основной статьи о формате определения запроса. Информация в этом документе может показаться плотной и сложной напервый взгляд.

В переполнении стека есть 6 вопросов, в которых упоминается DataMashup, и 4 из них относятся к Power BI, в то время какСхожие с этим вопросом не одинаковы.Ссылки на каждый из этих вопросов перечислены ниже:

Другие два вопроса более актуальны, поскольку они задают вопрос о Excel, а не Power BI, о котором я расскажу ниже:

  1. В этом вопросе задается вопрос, как удалить пользовательские данные XML запросов Power Query с помощью VBA.Я не хочу удалять запрос, а обновлять строку подключения, и я хотел бы сделать это в C #, а не VBA.Вопросы показывают результат с помощью устройства записи макросов, и я не хочу открывать каждый файл Excel для запуска макроса VBA.
  2. В этом вопросе задается вопрос, как найти информацию о запросе, и появляетсято же самое $Workbook$, что и я.В комментарии Акселя Рихтера он говорит In *.xlsx/customXml/ you will find a item1.xml which contains a DataMashup element which contains a base64Binary which is the binary query definition file. I have no clue how to work with that. That's why only a comment and not a answer. Через год Том Джебо добавил ответ, указывающий на подробности открытых спецификаций, которые я также нашел, но не предлагает решения о том, как манипулировать объектом DataMashup.Я добавляю это как новый вопрос, так как этот вопрос пытается решить немного другую проблему, чем я, и также ищет решение в JavaScript.

Каков наилучший способ декодированияобъект DataMashup, изменить имя сервера и затем сохранить обновленное соединение обратно в файл Excel?

В этом блоге Джеффа Этвуда от 1 июля 2011 года мы будем задавать вопросы и отвечать на ваши вопросы.Кроме того, эта страница из справочного центра Stack Overflow решает ту же проблему.Я решил опубликовать полностью рабочее решение на C #, чтобы другие могли его модифицировать и использовать, надеясь сэкономить им время, необходимое для работы со всеми разработками, которые я сделал.

1 Ответ

0 голосов
/ 17 июня 2019

Как уже упоминалось в вопросе, наиболее полезным документом является [MS-QDEFF]: Query Definition File Format.Я включу здесь наиболее важные части этого документа, но при необходимости сошлюсь на исходный документ.Ниже показан пример XML с DataMashup, предоставленный Microsoft.Это для короткого запроса, но ожидайте чего-то подобного, если вы откроете файл customXml > item1.xml.

<DataMashup sqmid="7690c5d6-5698-463c-a560-a0093d4f6332"
    xmlns="http://schemas.microsoft.com/DataMashup">
  AAAAAEUDAABQSwMEFAACAAgAta0pR62KRJynAAAA+QAAABIAHABDb25maWcvUGFja2FnZS54bWwgohgA
  KKAUAAAAAAAAAAAAAAAAAAAAAAAAAAAhY9NDoIwGESvQrqnP4jGkI+ycCuJCdG4bUqFRiiGFsvdXHgkr
  yCJYti5nMmb5M3r8YRsbJvgrnqrO5MihikKlJFdqU2VosFdwi3KOByEvIpKBRNsbDJanaLauVtCiPce+
  xXu+opElDJyzveFrFUrQm2sE0Yq9FuV/1eIw+kjwyMcxTimmzVmMWVA5h5ybRbMpIwpkEUJu6FxQ6+4M
  uGxADJHIN8b/A1QSwMEFAACAAgAta0pRw/K6aukAAAA6QAAABMAHABbQ29udGVudF9UeXBlc10ueG1sI
  KIYACigFAAAAAAAAAAAAAAAAAAAAAAAAAAAAG2OSw7CMAxErxJ5n7qwQAg1ZQHcgAtEwf2I5qPGReFsL
  DgSVyBtd4ilZ+Z55vN6V8dkB/GgMfbeKdgUJQhyxt961yqYuJF7ONbV9Rkoihx1UUHHHA6I0XRkdSx8I
  Jedxo9Wcz7HFoM2d90Sbstyh8Y7JseS5x9QV2dq9DSwuKQsr7UZB3Fac3OVAqbEuMj4l7A/eR3C0BvN2
  cQkbZR2IXEZXn8BUEsDBBQAAgAIALWtKUdi3rmEPAAAAEsAAAATABwARm9ybXVsYXMvU2VjdGlvbjEub
  SCiGAAooBQAAAAAAAAAAAAAAAAAAAAAAAAAAAArTk0uyczPUwiG0IbWvFy8XMUZiUWpKQqBpalFlYYKt
  go5qSW8XApAEJxfWpScChQx1Dbk5crMQxa1BgBQSwECLQAUAAIACAC1rSlHrYpEnKcAAAD5AAAAEgAAA
  AAAAAAAAAAAAAAAAAAAQ29uZmlnL1BhY2thZ2UueG1sUEsBAi0AFAACAAgAta0pRw/K6aukAAAA6QAAA
  BMAAAAAAAAAAAAAAAAA8wAAAFtDb250ZW50X1R5cGVzXS54bWxQSwECLQAUAAIACAC1rSlHYt65hDwAA
  ABLAAAAEwAAAAAAAAAAAAAAAADkAQAARm9ybXVsYXMvU2VjdGlvbjEubVBLBQYAAAAAAwADAMIAAABtA
  gAAAAA0AQAA77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48UGVybWlzc2lvb
  kxpc3QgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIge
  G1sbnM6eHNkPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSI+PENhbkV2YWx1YXRlRnV0d
  XJlUGFja2FnZXM+ZmFsc2U8L0NhbkV2YWx1YXRlRnV0dXJlUGFja2FnZXM+PEZpcmV3YWxsRW5hYmxlZ
  D50cnVlPC9GaXJld2FsbEVuYWJsZWQ+PFdvcmtib29rR3JvdXBUeXBlIHhzaTpuaWw9InRydWUiIC8+P
  C9QZXJtaXNzaW9uTGlzdD7LBwAAAAAAAKkHAADvu788P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nP
  SJ1dGYtOCI/PjxMb2NhbFBhY2thZ2VNZXRhZGF0YUZpbGUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczL
  m9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM6eHNkPSJodHRwOi8vd3d3LnczLm9yZy8yM
  DAxL1hNTFNjaGVtYSI+PEl0ZW1zPjxJdGVtPjxJdGVtTG9jYXRpb24+PEl0ZW1UeXBlPkFsbEZvcm11b
  GFzPC9JdGVtVHlwZT48SXRlbVBhdGggLz48L0l0ZW1Mb2NhdGlvbj48U3RhYmxlRW50cmllcyAvPjwvS
  XRlbT48SXRlbT48SXRlbUxvY2F0aW9uPjxJdGVtVHlwZT5Gb3JtdWxhPC9JdGVtVHlwZT48SXRlbVBhd
  Gg+U2VjdGlvbjEvUXVlcnkxPC9JdGVtUGF0aD48L0l0ZW1Mb2NhdGlvbj48U3RhYmxlRW50cmllcz48R
  W50cnkgVHlwZT0iSXNQcml2YXRlIiBWYWx1ZT0ibDAiIC8+PEVudHJ5IFR5cGU9IlJlc3VsdFR5cGUiI
  FZhbHVlPSJzTnVtYmVyIiAvPjxFbnRyeSBUeXBlPSJGaWxsRW5hYmxlZCIgVmFsdWU9ImwxIiAvPjxFb
  nRyeSBUeXBlPSJGaWxsVG9EYXRhTW9kZWxFbmFibGVkIiBWYWx1ZT0ibDAiIC8+PEVudHJ5IFR5cGU9I
  kZpbGxDb3VudCIgVmFsdWU9ImwxIiAvPjxFbnRyeSBUeXBlPSJGaWxsRXJyb3JDb3VudCIgVmFsdWU9I
  mwwIiAvPjxFbnRyeSBUeXBlPSJGaWxsQ29sdW1uVHlwZXMiIFZhbHVlPSJzQlE9PSIgLz48RW50cnkgV
  HlwZT0iRmlsbENvbHVtbk5hbWVzIiBWYWx1ZT0ic1smcXVvdDtRdWVyeTEmcXVvdDtdIiAvPjxFbnRye
  SBUeXBlPSJGaWxsRXJyb3JDb2RlIiBWYWx1ZT0ic1Vua25vd24iIC8+PEVudHJ5IFR5cGU9IkZpbGxMY
  XN0VXBkYXRlZCIgVmFsdWU9ImQyMDE1LTA5LTEwVDA0OjQ1OjQxLjkyNzU5MDBaIiAvPjxFbnRyeSBUe
  XBlPSJSZWxhdGlvbnNoaXBJbmZvQ29udGFpbmVyIiBWYWx1ZT0ic3smcXVvdDtjb2x1bW5Db3VudCZxd
  W90OzoxLCZxdW90O2tleUNvbHVtbk5hbWVzJnF1b3Q7OltdLCZxdW90O3F1ZXJ5UmVsYXRpb25zaGlwc
  yZxdW90OzpbXSwmcXVvdDtjb2x1bW5JZGVudGl0aWVzJnF1b3Q7OlsmcXVvdDtTZWN0aW9uMS9RdWVye
  TEvQXV0b1JlbW92ZWRDb2x1bW5zMS57UXVlcnkxLDB9JnF1b3Q7XSwmcXVvdDtDb2x1bW5Db3VudCZxd
  W90OzoxLCZxdW90O0tleUNvbHVtbk5hbWVzJnF1b3Q7OltdLCZxdW90O0NvbHVtbklkZW50aXRpZXMmc
  XVvdDs6WyZxdW90O1NlY3Rpb24xL1F1ZXJ5MS9BdXRvUmVtb3ZlZENvbHVtbnMxLntRdWVyeTEsMH0mc
  XVvdDtdLCZxdW90O1JlbGF0aW9uc2hpcEluZm8mcXVvdDs6W119IiAvPjxFbnRyeSBUeXBlPSJGaWxsZ
  WRDb21wbGV0ZVJlc3VsdFRvV29ya3NoZWV0IiBWYWx1ZT0ibDEiIC8+PEVudHJ5IFR5cGU9IkFkZGVkV
  G9EYXRhTW9kZWwiIFZhbHVlPSJsMCIgLz48RW50cnkgVHlwZT0iUmVjb3ZlcnlUYXJnZXRTaGVldCIgV
  mFsdWU9InNTaGVldDIiIC8+PEVudHJ5IFR5cGU9IlJlY292ZXJ5VGFyZ2V0Q29sdW1uIiBWYWx1ZT0ib
  DEiIC8+PEVudHJ5IFR5cGU9IlJlY292ZXJ5VGFyZ2V0Um93IiBWYWx1ZT0ibDEiIC8+PEVudHJ5IFR5c
  GU9Ik5hbWVVcGRhdGVkQWZ0ZXJGaWxsIiBWYWx1ZT0ibDAiIC8+PEVudHJ5IFR5cGU9IkZpbGxUYXJnZ
  XQiIFZhbHVlPSJzUXVlcnkxIiAvPjxFbnRyeSBUeXBlPSJCdWZmZXJOZXh0UmVmcmVzaCIgVmFsdWU9I
  mwxIiAvPjxFbnRyeSBUeXBlPSJGaWxsU3RhdHVzIiBWYWx1ZT0ic0NvbXBsZXRlIiAvPjxFbnRyeSBUe
  XBlPSJRdWVyeUlEIiBWYWx1ZT0iczdlMDQzNjJlLTkyZjUtNGQ4Mi04YjA3LTI3NjFlYWY2OGFlNSIgL
  z48L1N0YWJsZUVudHJpZXM+PC9JdGVtPjxJdGVtPjxJdGVtTG9jYXRpb24+PEl0ZW1UeXBlPkZvcm11b
  GE8L0l0ZW1UeXBlPjxJdGVtUGF0aD5TZWN0aW9uMS9RdWVyeTEvU291cmNlPC9JdGVtUGF0aD48L0l0Z
  W1Mb2NhdGlvbj48U3RhYmxlRW50cmllcyAvPjwvSXRlbT48L0l0ZW1zPjwvTG9jYWxQYWNrYWdlTWV0Y
  WRhdGFGaWxlPhYAAABQSwUGAAAAAAAAAAAAAAAAAAAAAAAA2gAAAAEAAADQjJ3fARXREYx6AMBPwpfrA
  QAAACLWGAG5O6FHjkAGtB+m5EQAAAAAAgAAAAAAA2YAAMAAAAAQAAAAaH8KNe2ciHwfVosIvSCr6gAAA
  AAEgAAAoAAAABAAAAA40fOKWe6kmTAWJSBXs4cYUAAAAPNy7uF6Dtr9PvADu+eZdeV7JutpIQTh41qqT
  3QnFoWPwE0Xyrur5N6Q2s2TEzjlBDfkEmNaGtr3htemOjWZYXKQHP+R5u/90zHWiwOwjjowFAAAAF2UC
  6Jm8C98hVmJBo638e4Qk65V
</DataMashup>

Значение этого объекта закодировано в строке Base64.Если вы не знакомы с Base 64, эта статья из Википедии будет хорошим началом.Первым шагом в решении будет открытие XML-документа и преобразование его в byte представление.Это можно сделать следующим образом:

string file = @"\customXml\item1.xml"; // or wherever your xml file is
XDocument doc = XDocument.Load(file);

byte[] dataMashup = Convert.FromBase64String(doc.Root.Value);

ПРИМЕЧАНИЕ: В полном примере, представленном внизу этого ответа, все манипуляции выполняются в памяти.

Из документа определения Microsoft:

Версия (4 байта): Целое число без знака, которое ДОЛЖНО быть установлено в 0.

Длина частей пакета (4байт): Целое число без знака, указывающее длину поля «Части пакета».

Части пакета (переменная): Двоичный поток переменной длины (раздел 2.3).

Длина разрешений (4 байта): Целое число без знака, указывающее длину поля Разрешения.

Разрешения (переменные): Двоичный поток переменной длины (раздел2.4).

Длина метаданных (4 байта): Целое число без знака, указывающее длину поля метаданных.

Метаданные (переменные): Двоичный поток переменной длины (раздел 2.5).

Длина привязок разрешений (4байт): Целое число без знака, указывающее длину поля Привязки разрешений.

Привязки разрешений (переменные): Двоичный поток переменной длины (раздел 2.6).

Так как каждое поле, которое определяет длину его содержимого, составляет 4 байта, я определил константу

private const int FIELDS_LENGTH = 4;

Тогда каждое из значений, определенных в этом разделе (цитируемом Microsoft), может быть найдено, как показанониже:

int version = BitConverter.ToUInt16(dataMashup.Take(FIELDS_LENGTH).ToArray(), 0);

int packagePartsLength = BitConverter.ToUInt16(dataMashup.Skip(FIELDS_LENGTH).Take(FIELDS_LENGTH).ToArray(), 0);
byte[] packageParts = dataMashup.Skip(FIELDS_LENGTH * 2).Take(packagePartsLength).ToArray();

int permissionsLength = BitConverter.ToUInt16(dataMashup.Skip(FIELDS_LENGTH  * 2 + packagePartsLength).Take(FIELDS_LENGTH).ToArray(), 0);
byte[] permissions = dataMashup.Skip(FIELDS_LENGTH * 3).Take(permissionsLength).ToArray();

int metadataLength = BitConverter.ToUInt16(dataMashup.Skip(FIELDS_LENGTH * 3 + packagePartsLength + permissionsLength).Take(FIELDS_LENGTH).ToArray(), 0);
byte[] metadata = dataMashup.Skip(FIELDS_LENGTH * 4 + packagePartsLength + permissionsLength).Take(metadataLength).ToArray();

int permissionsBindingLength = BitConverter.ToUInt16(dataMashup.Skip(FIELDS_LENGTH * 4 + packagePartsLength + permissionsLength + metadataLength).Take(FIELDS_LENGTH).ToArray(), 0);
byte[] permissionsBinding = dataMashup.Skip(FIELDS_LENGTH * 5 + packagePartsLength + permissionsLength + metadataLength).Take(permissionsBindingLength).ToArray();

Используя byte[] для частей пакета, он представляет объект Package из пространства имен System.IO.Packaging.

using (MemoryStream ms = new MemoryStream(packageParts)) {
    using (Package package = Package.Open(ms, FileMode.Open, FileAccess.ReadWrite)) {
        PackagePart section = package.GetParts().Where(x => x.Uri.OriginalString == "/Formulas/Section1.m").FirstOrDefault();

        string query;
        using (StreamReader reader = new StreamReader(section.GetStream())) {
            query = reader.ReadToEnd();
            // do other replacing, removing of query here
        }
        using (BinaryWriter writer = new BinaryWriter(section.GetStream())) {
            // write updated query back to package part
            writer.Write(Encoding.ASCII.GetBytes(query));
        }
    }

    packageParts = ms.ToArray();
}

Наконец, мне нужно обновить исходную byte[] новой информацией из обновленного пакета.

bytes = BitConverter.GetBytes(version)
            .Concat(BitConverter.GetBytes(packageParts.Length))
            .Concat(packageParts)
            .Concat(BitConverter.GetBytes(permissionsLength))
            .Concat(permissions)
            .Concat(BitConverter.GetBytes(metadataLength))
            .Concat(metadata)
            .Concat(BitConverter.GetBytes(permissionsBindingLength))
            .Concat(permissionsBinding);
doc.Root.Value = Convert.ToBase64String(bytes.ToArray());
entryStream.SetLength(0);
doc.Save(entryStream);

Ниже приведен полный пример полноты.Это консольное приложение, которое принимает каталог файлов для обновления в качестве аргументов командной строки, а затем заменяет старое имя сервера новым именем сервера.

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.IO.Compression;
using System.Xml.Linq;
using System.IO.Packaging;
using System.Text;

namespace MyApp {
    class Program {
        private const int FIELDS_LENGTH = 4;

        static void Main(string[] args) {
            if (args.Length != 1) {
                Console.WriteLine("specify one directory to update");
            }
            if (!Directory.Exists(args[0])) {
                Console.WriteLine("directory does not exist");
            }

            IEnumerable<FileInfo> files = Directory.GetFiles(args[0]).Where(x => Path.GetExtension(x) == ".xlsx").Select(x => new FileInfo(x));

            foreach (FileInfo file in files) {
                using (FileStream fileStream = File.Open(file.FullName, FileMode.OpenOrCreate)) {
                    using (ZipArchive archive = new ZipArchive(fileStream, ZipArchiveMode.Update)) {

                        ZipArchiveEntry entry = archive.GetEntry("customXml/item1.xml");

                        IEnumerable<byte> bytes;
                        using (Stream entryStream = entry.Open()) {
                            XDocument doc = XDocument.Load(entryStream);

                            byte[] dataMashup = Convert.FromBase64String(doc.Root.Value);
                            int version = BitConverter.ToUInt16(dataMashup.Take(FIELDS_LENGTH).ToArray(), 0);

                            int packagePartsLength = BitConverter.ToUInt16(dataMashup.Skip(FIELDS_LENGTH).Take(FIELDS_LENGTH).ToArray(), 0);
                            byte[] packageParts = dataMashup.Skip(FIELDS_LENGTH * 2).Take(packagePartsLength).ToArray();

                            int permissionsLength = BitConverter.ToUInt16(dataMashup.Skip(FIELDS_LENGTH * 2 + packagePartsLength).Take(FIELDS_LENGTH).ToArray(), 0);
                            byte[] permissions = dataMashup.Skip(FIELDS_LENGTH * 3).Take(permissionsLength).ToArray();

                            int metadataLength = BitConverter.ToUInt16(dataMashup.Skip(FIELDS_LENGTH * 3 + packagePartsLength + permissionsLength).Take(FIELDS_LENGTH).ToArray(), 0);
                            byte[] metadata = dataMashup.Skip(FIELDS_LENGTH * 4 + packagePartsLength + permissionsLength).Take(metadataLength).ToArray();

                            int permissionsBindingLength = BitConverter.ToUInt16(dataMashup.Skip(FIELDS_LENGTH * 4 + packagePartsLength + permissionsLength + metadataLength).Take(FIELDS_LENGTH).ToArray(), 0);
                            byte[] permissionsBinding = dataMashup.Skip(FIELDS_LENGTH * 5 + packagePartsLength + permissionsLength + metadataLength).Take(permissionsBindingLength).ToArray();

                            // use double memory stream to solve issue as memory stream will change
                            // size when re-saving the data mashup object
                            using (MemoryStream packagePartsStream = new MemoryStream(packageParts)) {
                                using (MemoryStream ms = new MemoryStream()) {
                                    packagePartsStream.CopyTo(ms);
                                    using (Package package = Package.Open(ms, FileMode.Open, FileAccess.ReadWrite)) {
                                        PackagePart section = package.GetParts().Where(x => x.Uri.OriginalString == "/Formulas/Section1.m").FirstOrDefault();

                                        string query;
                                        using (StreamReader reader = new StreamReader(section.GetStream())) {
                                            query = reader.ReadToEnd();
                                            // do other replacing, removing of query here
                                            query = query.Replace("old-server", "new-server");
                                        }
                                        using (BinaryWriter writer = new BinaryWriter(section.GetStream())) {
                                            writer.Write(Encoding.ASCII.GetBytes(query));
                                        }
                                    }

                                    packageParts = ms.ToArray();
                                }

                                bytes = BitConverter.GetBytes(version)
                                            .Concat(BitConverter.GetBytes(packageParts.Length))
                                            .Concat(packageParts)
                                            .Concat(BitConverter.GetBytes(permissionsLength))
                                            .Concat(permissions)
                                            .Concat(BitConverter.GetBytes(metadataLength))
                                            .Concat(metadata)
                                            .Concat(BitConverter.GetBytes(permissionsBindingLength))
                                            .Concat(permissionsBinding);
                                doc.Root.Value = Convert.ToBase64String(bytes.ToArray());
                                entryStream.SetLength(0);
                                doc.Save(entryStream);
                            }
                        }
                    }
                }
            }
        }
    }
}

ПРИМЕЧАНИЕ: Как янужно только обновить часть Package Parts, я могу подтвердить, что это декодирование / кодирование работает, но я не тестировал декодирование / кодирование Permissions, Metadata или Permissions Binding.Если вам нужно использовать их, это должно как минимум начать.

ПРИМЕЧАНИЕ: Этот код не перехватывает ошибки и не обрабатывает все случаи.Это рабочий пример того, как обновить соединения в файле Power Query.Не стесняйтесь адаптировать это как вам нужно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...