Программирование семантических моделей Power BI с помощью табличной объектной модели (TOM)

Применимо к: SQL Server 2016 и более поздних версий Analysis Services Azure Analysis Services Fabric/Power BI Premium

Эта статья была первоначально создана группой по консультированию клиентов Power BI (CAT) для Power BI Dev Camp. Это коллекция сеансов, статей и видеороликов о расширенном программировании для Power BI.

Power BI Premium семантические модели включают конечную точку XMLA. Конечная точка важна для разработчиков Power BI, так как она предоставляет API-интерфейсы для взаимодействия с подсистемой служб Analysis Services, работающей в службе Power BI, и для непосредственного программирования моделей Power BI. Все больше специалистов в Power BI обнаружили, что они могут создавать и просматривать модели Power BI, а также управлять ими с помощью уже существующих средств, использующих протокол XMLA, таких как SQL Server Management Studio, табличный редактор и DAX Studio. Разработчик .NET теперь может писать код C# в приложении .NET для создания и изменения моделей непосредственно в службе Power BI.

Табличная объектная модель (TOM) — это библиотека .NET, которая предоставляет абстрактный слой поверх конечной точки XMLA. Она позволяет разработчикам писать код с точки зрения интуитивно понятной модели программирования, которая включает такие классы, как Model, Table, Column и Measure. В фоновом режиме TOM преобразует операции чтения и записи в коде в HTTP-запросы, выполняемые к конечной точке XMLA.

Схема приложения для моделирования через конечную точку XMLA.

В этой статье основное внимание уделяется началу работы с TOM и демонстрации того, как написать код C#, необходимый для создания и изменения моделей во время их работы в службе Power BI. Однако TOM также можно использовать в сценариях, не использующих конечную точку XMLA, например при программировании на основе локальной модели, работающей в Power BI Desktop. Дополнительные сведения об использовании TOM с Power BI Desktop см. в серии блогов члена Power BI CAT Фила Seamark и watch видео Как программировать наборы данных с помощью табличной объектной модели (TOM) из Лагеря разработчиков Power BI.

TOM представляет новый и мощный API для разработчиков Power BI, который отделен и отличается от REST API Power BI. Хотя между этими двумя API есть некоторое перекрытие, каждый из этих API включает значительный объем функциональных возможностей, не включенных в другой. Кроме того, существуют сценарии, требующие от разработчика совместного использования обоих API для реализации полного решения.

начало работы с табличной объектной моделью

Первое, что необходимо получить, прежде чем вы сможете программировать с помощью TOM, это URL-адрес для подключения к рабочей области. URL-адрес подключения к рабочей области ссылается на определенную рабочую область и используется для создания строка подключения, который позволяет коду подключаться к этой рабочей области Power BI и моделям, работающим в ней. Для начала перейдите на страницу Параметры рабочей области Power BI, работающей в выделенной емкости.

Ссылка на параметры рабочей области.

Примечание

Конечная точка XMLA поддерживается только для моделей, работающих в выделенной емкости. Он недоступен для моделей, работающих в общей емкости. При работе с моделями в Power BI Premium емкости на пользователя можно подключиться как пользователь, но нельзя подключиться как субъект-служба.

После перехода на вкладку Премиум области Параметры скопируйте URL-адрес подключения рабочей области в буфер обмена.

Строка подключения рабочей области в параметрах семантической модели.

Следующим шагом является создание нового приложения .NET, в котором вы пишете код C#, который программируется с помощью TOM. Вы можете создать веб-приложение или классическое приложение с помощью .NET 5, .NET Core 3.1 или более ранних версий на платформа .NET Framework. В этой статье мы создадим простое консольное приложение C# с помощью пакета SDK для .NET 5.

Создание нового консольного приложения

Начните с создания консольного приложения с помощью .NET CLI .

dotnet new console --name`

Добавление пакета NuGet табличной объектной модели

После создания консольного приложения добавьте пакет NuGet Microsoft.AnalysisServices.AdomdClient.NetCore.retail.amd64 , содержащий табличную объектную модель (TOM). Пакет можно установить в приложении .NET 5 с помощью следующего интерфейса командной строки .NET:

dotnet add package Microsoft.AnalysisServices.NetCore.retail.amd64

Добавление строки подключения

Если в проекте установлен пакет NuGet с библиотекой TOM, можно создать традиционное приложение Hello World с помощью TOM. Приложение подключается к рабочей области Power BI по URL-адресу подключения к рабочей области, а затем перечисляет модели в рабочей области и отображает их имена в окне консоли.

using System;
using Microsoft.AnalysisServices.Tabular;

class Program {
  static void Main() {

    // create the connect string
    string workspaceConnection = "powerbi://api.powerbi.com/v1.0/myorg/LearningTOM";
    string connectString = $"DataSource={workspaceConnection};";

    // connect to the Power BI workspace referenced in connect string
    Server server = new Server();
    server.Connect(connectString);

    // enumerate through models in workspace to display their names
    foreach (Database database in server.Databases) {
      Console.WriteLine(database.Name);
    }
  }
}

В этом примере строка подключения содержит URL-адрес подключения к рабочей области, но не содержит сведений о пользователе. Если вы запускаете консольное приложение с этим кодом, оно начнет выполняться, а затем вам будет предложено войти в систему в браузере. При входе с помощью учетной записи пользователя, у которого есть разрешения на доступ к рабочей области, на которую ссылается URL-адрес подключения к рабочей области, библиотека TOM сможет получить маркер доступа, подключиться к службе Power BI и перечислить модели в рабочей области.

Дополнительные сведения о подключении через конечную точку XMLA см. в статье Подключение sematic model к конечной точке XMLA — подключение к рабочей области Premium.

Проверка подлинности с помощью имени пользователя и пароля

В сценариях разработки и тестирования, в которых безопасность не так важна, можно жестко задать имя пользователя и пароль, устраняя необходимость интерактивного входа в систему при каждом запуске программы для тестирования кода, как показано в следующем коде:

string workspaceConnection = "powerbi://api.powerbi.com/v1.0/myorg/YOUR_WORKSPACE";
string userId = "YOUR_USER_NAME";
string password = "YOUR_USER_PASSWORD";
string connectStringUser = $"DataSource={workspaceConnection};User ID={userId};Password={password};";
server.Connect(connectStringUser);

Проверка подлинности с помощью субъекта-службы

Кроме того, довольно легко пройти проверку подлинности как субъект-службу, а не как пользователь. Если вы создали приложение Microsoft Entra с идентификатором приложения и секретом приложения, вы можете пройти проверку подлинности кода для запуска в качестве субъекта-службы для приложения Microsoft Entra с помощью следующего примера кода:

string workspaceConnection = "powerbi://api.powerbi.com/v1.0/myorg/YOUR_WORKSPACE";
string tenantId = "YOUR_TENANT_ID";
string appId = "YOUR_APP_ID";
string appSecret = "YOUR_APP_SECRET";
string connectStringApp = $"DataSource={workspaceConnection};User ID=app:{appId}@{tenantId};Password={appSecret};";
server.Connect(connectStringApp);

Чтобы программировать с помощью TOM и получать доступ к модели в качестве субъекта-службы, необходимо настроить параметр Power BI на уровне клиента на портале Администратор Power BI. Действия по настройке Power BI для поддержки подключения в качестве субъекта-службы описаны в статье Внедрение содержимого Power BI с помощью субъекта-службы и секрета приложения.

Проверка подлинности с помощью маркера доступа Microsoft Entra

ToM также обеспечивает гибкость при установке подключения с использованием допустимого маркера доступа Microsoft Entra. Если у вас есть навыки разработчика для реализации потока проверки подлинности с идентификатором Microsoft Entra и получения маркеров доступа, вы можете отформатировать строка подключения TOM без имени пользователя, но включить маркер доступа в качестве пароля, как показано в следующем примере кода:

public static void ConnectToPowerBIAsUser() {
  string workspaceConnection = "powerbi://api.powerbi.com/v1.0/myorg/YOUR_WORKSPACE";
  string accessToken = TokenManager.GetAccessToken();  // you must implement GetAccessToken yourself
  string connectStringUser = $"DataSource={workspaceConnection};Password={accessToken};";
  server.Connect(connectStringUser);
}

Если вы приобретаете маркер доступа на основе пользователя для подключения к рабочей области Power BI с помощью TOM, обязательно запросите следующие делегированные разрешения при получении маркера доступа, чтобы убедиться, что у вас есть все необходимые разрешения на разработку:

public static readonly string[] XmlaScopes = new string[] {
    "https://analysis.windows.net/powerbi/api/Content.Create",
    "https://analysis.windows.net/powerbi/api/Dataset.ReadWrite.All",
    "https://analysis.windows.net/powerbi/api/Workspace.ReadWrite.All",
};

Если вы программировали с помощью REST API Power BI, вы можете распознать знакомые разрешения, такие как Content.Create, Dataset.ReadWrite.All и Workspace.ReadWrite.All. Интересное замечание заключается в том, что TOM использует тот же набор делегированных разрешений, что и REST API Power BI, определенный в область идентификатора https://analysis.windows.net/powerbi/apiресурса Microsoft Entra .

Тот факт, что и конечная точка XMLA, и REST API Power BI используют один и тот же набор делегированных разрешений, имеет свои преимущества. Маркеры доступа можно использовать взаимозаменяемо между TOM и REST API Power BI. Получив маркер доступа для вызова TOM для создания новой модели, вы можете использовать тот же маркер доступа для вызова REST API Power BI, чтобы задать учетные данные источника данных, как описано далее в этой статье.

Одна вещь, которая, как правило, смущает программистов Power BI, заключается в том, что субъекты-службы не используют делегированные разрешения. Вместо этого при программировании с помощью TOM вы настраиваете доступ к субъекту-службе, добавляя его в целевую рабочую область в качестве члена в роли Администратор или участника.

Основные сведения об объектах сервера, базы данных и модели

Объектная модель в TOM основана на иерархии с объектом Server верхнего уровня, который содержит коллекцию объектов Database . При программировании с помощью TOM в Power BI объект Server представляет рабочую область Power BI, а объект Database представляет модель Power BI.

Схема табличной объектной модели со всеми объектами

Каждая база данных содержит объект Model , который предоставляет доступ на чтение и запись к модели данных. Модель содержит коллекции для элементов модели данных, включая DataSource, Table, Relationship, Perspective, Culture и Role.

Как показано в коде Hello World, после вызова сервера. Подключитесь, чтобы легко узнать, какие модели существуют в рабочей области Power BI, выполнив перечисление через коллекцию Databases объекта Server, как показано в следующем коде:

foreach (Database database in server.Databases) {
    Console.WriteLine(database.Name);
}

Вы также можете использовать метод GetByName , предоставляемый объектом коллекции Databases , для доступа к модели по имени, как показано ниже:

Database database = server.Databases.GetByName("Wingtip Sales");

Важно различать объект Databaseи его внутреннее свойство Model . Свойства объекта базы данных можно использовать для обнаружения атрибутов модели, таких как Name, ID, CompatibilityMode и CompatibilityLevel. Существует также свойство EstimatedSize , которое позволяет узнать, насколько велика модель. К другим свойствам относятся LastUpdate, LastProcessed и LastSchemaUpdate , которые позволяют определить время последнего обновления базовой модели и время последнего обновления схемы модели.

public static void GetDatabaseInfo(string DatabaseName) {
  Database database = server.Databases.GetByName(DatabaseName);
  Console.WriteLine("Name: " + database.Name);
  Console.WriteLine("ID: " + database.ID);
  Console.WriteLine("CompatibilityMode: " + database.CompatibilityMode);
  Console.WriteLine("CompatibilityLevel: " + database.CompatibilityLevel);
  Console.WriteLine("EstimatedSize: " + database.EstimatedSize);
  Console.WriteLine("LastUpdated: " + database.LastUpdate);
  Console.WriteLine("LastProcessed: " + database.LastProcessed);
  Console.WriteLine("LastSchemaUpdate: " + database.LastSchemaUpdate);
}

Хотя объект Database имеет свои собственные свойства, он является внутренним объектом Model объекта Database , который предоставляет возможность чтения и записи в базовую модель данных модели. Ниже приведен простой пример программирования объекта модели базы данных для перечисления по его коллекции Tables и обнаружения таблиц, которые находятся внутри.

В объектной модели TOM каждый объект Table имеет объекты коллекции для своих секций. столбцы, меры и иерархии.

Схема табличной объектной модели с таблицей, разделом, столбцом, мерой и иерархией

Получив объект Model для базы данных, вы можете получить доступ к определенной таблице по имени в модели с помощью метода Find коллекции Tables . Ниже приведен пример получения таблицы с именем Sales и обнаружения ее членов путем перечисления через коллекции Columns и Measures :

Model databaseModel = server.Databases.GetByName("Tom Demo").Model;

Table tableSales = databaseModel.Tables.Find("Sales");

foreach (Column column in tableSales.Columns) {
  Console.WriteLine("Coulumn: " + column.Name);
}

foreach (Measure measure in tableSales.Measures) {
  Console.WriteLine("Measure: " + measure.Name);
  Console.WriteLine(measure.Expression);
}

Изменение моделей с помощью TOM

В разделах выше вы узнали, как получить доступ к объекту Database и его объекту Model для проверки модели, работающей в службе Power BI. Теперь пора запрограммировать наше первое обновление модели с помощью TOM, добавив меру в таблицу.

Используемая емкость должна быть включена для чтения и записи XMLA. По умолчанию для параметра разрешений конечной точки XMLA задано значение Чтение, поэтому пользователь с разрешениями На Администратор емкости должен явно задать значение Чтение записи. Этот параметр можно просмотреть и обновить на странице Параметры емкости на портале Администратор.

Параметр записи чтения XMLA на портале Администратор.

После настройки конечной точки XMLA для чтения и записи можно добавить новую меру с именем Sales Revenue в таблицу Sales , как показано в следующем коде:

Model dataset = server.Databases.GetByName("Tom Demo Starter").Model;
Table tableSales = dataset.Tables.Find("Sales");
Measure salesRevenue = new Measure();
salesRevenue.Name = "Sales Revenue";
salesRevenue.Expression = "SUM(Sales[SalesAmount])";
salesRevenue.FormatString = "$#,##0.00";
tableSales.Measures.Add(salesRevenue);
dataset.SaveChanges();

Давайте подробнее рассмотрим этот код. Сначала вы создаете новый объект Measure с помощью оператора C# new и предоставляете значения для Name, Expression и FormatString. Затем вы добавляете новый объект Measure в коллекцию Measures целевого объекта Table , вызвав метод Add . Наконец, вызовите метод SaveChanges объекта Model , чтобы записать изменения обратно в модель в службе Power BI.

Помните, что обновления модели пакетируются в памяти до вызова SaveChanges. Представьте сценарий, в котором вы хотите скрыть каждый столбец в таблице. Для начала можно написать цикл foreach для перечисления всех объектов Column для таблицы и задать свойству IsHidden для каждого объекта Column значение true. После завершения цикла foreach вы запустите несколько обновлений столбцов, которые пакетируются в памяти. Но это последний вызов SaveChanges , который отправляет все изменения обратно в службу Power BI в пакете, как показано ниже:

Model dataset = server.Databases.GetByName("Tom Demo").Model;
Table tableSales = dataset.Tables.Find("Sales");

foreach (Column column in tableSales.Columns) {
  column.IsHidden = true;
}

dataset.SaveChanges();

Предположим, вы хотите обновить свойство FormatString для существующего столбца. Коллекция Columns предоставляет метод Find для получения целевого объекта Column . После этого нужно задать свойство FormatString и вызвать SaveChanges следующим образом:

Model dataset = server.Databases.GetByName("Tom Demo").Model;
Table tableSales = dataset.Tables.Find("Products");
Column columnListPrice = tableSales.Columns.Find("List Price");
columnListPrice.FormatString = "$#,##0.00";
dataset.SaveChanges();

Способность TOM динамически обнаруживать, что находится в модели, предоставляет возможности для выполнения обновлений универсальным и радикальным образом. Представьте себе сценарий, в котором вы управляете моделью, которая содержит большое количество таблиц и даже сотни столбцов на основе типа данных DateTime . Вы можете обновить свойство FormatString для каждого столбца DateTime во всей модели одновременно, используя следующее:

Database database = server.Databases.GetByName("Tom Demo Starter");
Model datasetModel = database.Model;

foreach (Table table in datasetModel.Tables) {
  foreach (Column column in table.Columns) {
    if(column.DataType == DataType.DateTime) {
      column.FormatString = "yyyy-MM-dd";
    }
  }
}

datasetModel.SaveChanges();

Обновление моделей с помощью TOM

Теперь давайте выполним типичную операцию обслуживания модели. Как показано в следующем коде, запустить операцию обновления модели с помощью TOM не так сложно:

public static void RefreshDatabaseModel(string Name) {
  Database database = server.Databases.GetByName(Name);
  database.Model.RequestRefresh(RefreshType.DataOnly);
  database.Model.SaveChanges();
}

Как и при ручном и запланированном обновлении модели, обновления через конечную точку XMLA отображаются в журнале обновления, но с меткой Через конечную точку XMLA.

Диалоговое окно обновления журнала

Примечание

Хотя TOM предоставляет возможность запуска операции обновления, она не может задать учетные данные источника данных для модели Power BI. Чтобы обновить модели с помощью TOM, необходимо сначала задать учетные данные источника данных в параметрах семантической модели или с помощью REST API Power BI.

Создание и клонирование моделей

Представьте, что у вас есть требование создавать и клонировать модели Power BI с помощью кода, написанного на C#. Начнем с написания повторной функции с именем CreateDatabase , которая создает новый объект Database , как показано ниже:

public static Database CreateDatabase(string DatabaseName) {

  string newDatabaseName = server.Databases.GetNewName(DatabaseName);
  var database = new Database() {
    Name = newDatabaseName,
    ID = newDatabaseName,
    CompatibilityLevel = 1520,
    StorageEngineUsed = Microsoft.AnalysisServices.StorageEngineUsed.TabularMetadata,
    Model = new Model() {
      Name = DatabaseName + "-Model",
      Description = "A Demo Tabular data model with 1520 compatibility level."
    }
  };

  server.Databases.Add(database);
  database.Update(Microsoft.AnalysisServices.UpdateOptions.ExpandFull);
  return database;

}

В этом примере мы начнем с использования метода GetNewNameобъекта коллекции Databases , чтобы убедиться, что новое имя модели уникально в целевой рабочей области. После этого объект Database и его объект Model можно создать с помощью оператора C# new , как показано в коде ниже. В конце этот метод добавляет новый объект Database в коллекцию Databases и вызывает базу данных. Метод update .

Если вы хотите скопировать существующую модель вместо создания новой, можно использовать следующий метод CopyDatabase , чтобы клонировать модель Power BI, создав новую пустую модель, а затем вызвав CopyTo в объекте Model для исходной модели, чтобы скопировать всю модель данных в только что созданную модель.

public static Database CopyDatabase(string sourceDatabaseName, string DatabaseName) {
  Database sourceDatabase = server.Databases.GetByName(sourceDatabaseName);
  string newDatabaseName = server.Databases.GetNewName(DatabaseName);
  Database targetDatabase = CreateDatabase(newDatabaseName);
  sourceDatabase.Model.CopyTo(targetDatabase.Model);
  targetDatabase.Model.SaveChanges();
  targetDatabase.Model.RequestRefresh(RefreshType.Full);
  targetDatabase.Model.SaveChanges();
  return targetDatabase;
}

Создание реальной модели с нуля

Теперь представьте, что вы только что создали новую модель с нуля и теперь вам нужно использовать TOM для создания реальной модели данных путем добавления таблиц, столбцов, мер, иерархий и связей таблиц. Рассмотрим пример кода, который создает новую таблицу, которая включает определенные столбцы, добавляет трехмерную многомерную иерархию и даже предоставляет выражение M для запроса базовой таблицы:

private static Table CreateProductsTable() {

  Table productsTable = new Table() {
    Name = "Products",
    Description = "Products table",
    Partitions = {
      new Partition() {
        Name = "All Products",
        Mode = ModeType.Import,
        Source = new MPartitionSource() {
          // M code for query maintained in separate source file
          Expression = Properties.Resources.ProductQuery_m
        }
      }
    },
    Columns = {
      new DataColumn() { Name = "ProductId", DataType = DataType.Int64, SourceColumn = "ProductId", IsHidden = true },
      new DataColumn() { Name = "Product", DataType = DataType.String, SourceColumn = "Product" },
      new DataColumn() { Name = "Description", DataType = DataType.String, SourceColumn = "Description" },
      new DataColumn() { Name = "Category", DataType = DataType.String, SourceColumn = "Category" },
      new DataColumn() { Name = "Subcategory", DataType = DataType.String, SourceColumn = "Subcategory" },
      new DataColumn() { Name = "Product Image", DataType = DataType.String, 
                        SourceColumn = "ProductImageUrl", DataCategory = "ImageUrl" }
     }
  };

  productsTable.Hierarchies.Add(
    new Hierarchy() {
      Name = "Product Category",
      Levels = {
        new Level() { Ordinal=0, Name="Category", Column=productsTable.Columns["Category"] },
        new Level() { Ordinal=1, Name="Subcategory", Column=productsTable.Columns["Subcategory"] },
        new Level() { Ordinal=2, Name="Product", Column=productsTable.Columns["Product"] }
      }
  });

  return productsTable;
}

После создания набора вспомогательных методов для создания таблиц их можно объединить для создания модели данных следующим образом:

Model model = database.Model;
Table tableCustomers = CreateCustomersTable();
Table tableProducts = CreateProductsTable();
Table tableSales = CreateSalesTable();
Table tableCalendar = CreateCalendarTable();
model.Tables.Add(tableCustomers);
model.Tables.Add(tableProducts);
model.Tables.Add(tableSales);
model.Tables.Add(tableCalendar);

TOM предоставляет коллекцию Relationships в объекте Model , которая позволяет определить связи между таблицами в модели. Ниже приведен код, необходимый для создания объекта SingleColumnRelationship , который устанавливает связь "один ко многим " между таблицей Products и таблицей Sales :

model.Relationships.Add(new SingleColumnRelationship {
  Name = "Products to Sales",
  ToColumn = tableProducts.Columns["ProductId"],
  ToCardinality = RelationshipEndCardinality.One,
  FromColumn = tableSales.Columns["ProductId"],
  FromCardinality = RelationshipEndCardinality.Many
});

Завершив добавление таблиц и связей между таблицами, сохраните свою работу с вызовом модели. SaveChanges:

model.SaveChanges();

На этом этапе после вызова SaveChanges вы сможете увидеть новую модель, созданную в службе Power BI, и начать использовать ее для создания новых отчетов.

Отчет модели в служба Power BI.

Важно!

Помните, что перед обновлением модели необходимо указать учетные данные источника данных в параметрах семантической модели или с помощью REST API Power BI.

Пример проекта

Пример проекта с кодом C#, который вы видели в этой статье, доступен здесь. Теперь пришло время начать программирование с помощью TOM и найти способы использования этого нового мощного API при разработке пользовательских решений для Power BI.

См. также раздел

Подключение семантической модели к конечной точке XMLA
Устранение неполадок с подключением конечных точек XMLA