表形式オブジェクト モデル (TOM) を使用した Power BI セマンティック モデルのプログラミング

適用対象:SQL Server 2016 以降の Analysis Services Azure Analysis Services Fabric/Power BI Premium

この記事は、もともと Power BI の高度なプログラミングに関するセッション、記事、ビデオのコレクションである Power BI Dev Camp 用の Power BI カスタマー アドバイザリ チーム (CAT) によって作成されました。

Power BI Premiumセマンティック モデルには、XMLA エンドポイントが含まれます。 このエンドポイントは、Power BI サービスで実行されている Analysis Services エンジンと対話し、Power BI モデルに対して直接プログラミングするための API を提供するため、Power BI 開発者にとって重要です。 多くの Power BI プロフェッショナルは、SQL Server Management Studio、表形式エディター、DAX Studio などの XMLA プロトコルを使用する既存のツールを使用して Power BI モデルを作成、表示、管理できることを発見しました。 .NET 開発者は、.NET アプリケーションで C# コードを記述して、Power BI サービスでモデルを直接作成および変更できるようになりました。

表形式オブジェクト モデル (TOM) は、XMLA エンドポイントの上に抽象レイヤーを提供する .NET ライブラリです。 これにより、開発者は、 ModelTableColumnMeasure などのクラスを含む直感的なプログラミング モデルの観点からコードを記述できます。 TOM はバックグラウンドで、コード内の読み取り操作と書き込み操作を XMLA エンドポイントに対して実行される HTTP 要求に変換します。

XMLA エンドポイントを介してモデル化するアプリケーションの図。

この記事の焦点は、TOM の使用を開始し、Power BI サービスで実行している間にモデルを作成および変更するために必要な C# コードを記述する方法を示すことです。 ただし、TOM は、POWER BI DESKTOPで実行されているローカル モデルに対してプログラミングする場合など、XMLA エンドポイントを含まないシナリオでも使用できます。 Power BI Desktopで TOM を使用する方法の詳細については、Power BI CAT メンバー Phil Seamark のブログ シリーズを参照し、Power BI Dev Camp の表形式オブジェクト モデル (TOM) を使用してデータセットをプログラミングする方法に関するビデオをwatchしてください。

TOM は、Power BI REST API とは別の Power BI 開発者向けの新しく強力な API を表します。 これら 2 つの API の間にはいくつかの重複がありますが、これらの API にはそれぞれ、他の API に含まれていない大量の機能が含まれています。 さらに、開発者が両方の API を組み合わせて使用して完全なソリューションを実装する必要があるシナリオもあります。

表形式オブジェクト モデルを使用したはじめに

TOM を使用してプログラムする前に最初に取得する必要があるのは、ワークスペース接続の URL です。 ワークスペース接続 URL は特定のワークスペースを参照し、コードがその Power BI ワークスペースと内部で実行されているモデルに接続できる接続文字列を作成するために使用されます。 まず、専用容量で実行されている Power BI ワークスペースの [設定] ページに移動します。

ワークスペース設定へのリンク。

注意

XMLA エンドポイントは、専用容量で実行されているモデルでのみサポートされています。 共有容量で実行されているモデルでは使用できません。 ユーザー容量あたりのPower BI Premiumでモデルを操作する場合は、ユーザーとして接続できますが、サービス プリンシパルとして接続することはできません。

[設定] ウィンドウの [Premium] タブに移動したら、[ワークスペース接続 URL] をクリップボードにコピーします。

セマンティック モデルの [設定] のワークスペース 接続文字列。

次の手順では、TOM を使用してプログラムする C# コードを記述する新しい .NET アプリケーションを作成します。 .NET Frameworkで .NET 5、.NET Core 3.1、または以前のバージョンを使用して、Web アプリケーションまたはデスクトップ アプリケーションを作成できます。 この記事では、 .NET 5 SDK を使用して単純な C# コンソール アプリケーションを作成します。

新しいコンソール アプリケーションを作成する

まず 、.NET CLI を使用して新しいコンソール アプリケーションを作成します。

dotnet new console --name`

表形式オブジェクト モデル NuGet パッケージを追加する

コンソール アプリケーションを作成した後、表形式オブジェクト モデル (TOM) を含む Microsoft.AnalysisServices.AdomdClient.NetCore.retail.amd64 NuGet パッケージを追加します。 次の .NET CLI を使用して、.NET 5 アプリケーションにパッケージをインストールできます。

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

接続文字列を追加する

プロジェクトに TOM ライブラリがインストールされた NuGet パッケージがある場合は、TOM を使用して従来のHello World アプリケーションを作成できます。 アプリケーションは、ワークスペース接続 URL を使用して Power BI ワークスペースに接続し、ワークスペース内のモデルを列挙して、その名前をコンソール ウィンドウに表示します。

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 エンドポイントを介した接続の詳細については、「 XMLA エンドポイントとの Sematic モデル接続 - 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);

サービス プリンシパルを使用した認証

また、ユーザーとしてではなく、サービス プリンシパルとして認証することも非常に簡単です。 アプリケーション ID とアプリケーション シークレットを使用して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 ID を使用して認証フローを実装し、アクセス トークンを取得する開発者スキルがある場合は、ユーザー名なしで 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);
}

TOM を使用して Power BI ワークスペースに接続するためのユーザーベースのアクセス トークンを取得する場合は、アクセス トークンを取得するときに次の委任されたアクセス許可を要求して、必要なすべてのオーサリング アクセス許可があることを確認してください。

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",
};

Power BI REST API を使用してプログラミングを行っている場合は、Content.Create、Dataset.ReadWrite.AllWorkspace.ReadWrite.All などの使い慣れたアクセス許可を認識している可能性があります。 興味深いことに、TOM では、Microsoft Entra リソース ID のスコープ内で定義されている Power BI REST API と同じ委任されたアクセス許可のhttps://analysis.windows.net/powerbi/apiセットが使用されます。

XMLA エンドポイントと Power BI REST API の両方が、委任されたアクセス許可の同じセットを共有するという事実には、その利点があります。 アクセス トークンは、TOM と Power BI REST API の間で同じ意味で使用できます。 TOM を呼び出して新しいモデルを作成するためのアクセス トークンを取得したら、同じアクセス トークンを使用して Power BI REST API を呼び出してデータ ソースの資格情報を設定できます。これについては、この記事で後述します。

Power BI プログラマを混乱させる傾向がある 1 つは、サービス プリンシパルが委任されたアクセス許可を使用しないことです。 代わりに、TOM を使用してプログラミングする場合は、サービス プリンシパルをターゲット ワークスペースに追加して、管理またはメンバーのロールのメンバーとしてサービス プリンシパルへのアクセスを構成します。

サーバー、データベース、およびモデル オブジェクトについて

TOM のオブジェクト モデルは、Database オブジェクトのコレクションを含む最上位レベルの Server オブジェクトを持つ階層に基づいています。 Power BI で TOM を使用してプログラミングする場合、 Server オブジェクトは Power BI ワークスペースを表し、 Database オブジェクトは Power BI モデルを表します。

すべてのオブジェクトを含む表形式オブジェクト モデル図

データベース には、データ モデル への読み取り/書き込みアクセスを提供する Model オブジェクトが含まれています。 Model には、DataSourceTableRelationshipPerspectiveCultureRole などのデータ モデルの要素のコレクションが含まれています。

Hello World コードに示すように、サーバーを呼び出すとします。接続すると、次のコードに示すように、Server オブジェクトの Databases コレクションを列挙することで、Power BI ワークスペース内に存在するモデルを簡単に検出できます。

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

Databases コレクション オブジェクトによって公開される GetByName メソッドを使用して、次のように名前でモデルにアクセスすることもできます。

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

Databaseオブジェクトとその内部 Model プロパティを区別することが重要です。 Database オブジェクトのプロパティを使用して、NameIDCompatibilityMode、CompatibilityLevel などのモデル属性を検出できます。 EstimatedSize プロパティもあり、モデルの大きさを確認できます。 その他のプロパティには、 LastUpdateLastProcessedLastSchemaUpdate があります。このプロパティを使用すると、基になるモデルが最後に更新された日時と、モデル スキーマが最後に更新された日時を判断できます。

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 オブジェクトには独自のプロパティがあります。これは Database オブジェクトの内部 Model オブジェクトであり、モデルの基になるデータ モデルに対する読み取りと書き込みを行うことができます。 ここでは、データベース Model オブジェクトをプログラミングして Tables コレクションを列挙し、内部のテーブルを検出する簡単な例を示します。

TOM オブジェクト モデルでは、各 Table オブジェクトにはパーティションのコレクション オブジェクトがあります。 列、メジャー、階層。

テーブル、パーティション、列、メジャー、階層を含む表形式オブジェクト モデル図

DatabaseModel オブジェクトを取得したら、Tables コレクションの Find メソッドを使用して、モデル内の特定のテーブルに名前でアクセスできます。 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();

このコードを詳しく見てみましょう。 まず、C# new 演算子を使用して新しい Measure オブジェクトを作成し、NameExpressionFormatString の値を指定します。 次に、Add メソッドを呼び出して、ターゲット Table オブジェクトの Measures コレクションに新しい Measure オブジェクトを追加します。 最後に、Model オブジェクトの SaveChanges メソッドを呼び出して、変更を Power BI サービスのモデルに書き戻します。

SaveChanges を呼び出すまで、モデルの更新はメモリにバッチ処理されます。 テーブル内のすべての列を非表示にするシナリオを想像してください。 まず、foreach ループを記述して、テーブルのすべての Column オブジェクトを列挙し、各 Column オブジェクトの IsHidden プロパティを true に設定します。 foreach ループが完了すると、メモリ内でバッチ処理される複数の列の更新が行われます。 ただし、次に示すように、すべての変更をバッチで Power BI サービスにプッシュバックする SaveChanges の最後の呼び出しです。

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 コレクションは、ターゲットの Column オブジェクトを取得する Find メソッドを公開します。 その後、 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 データ型に基づいて多数のテーブルと数十列、さらには数百列のモデルを管理しているシナリオを想像してみてください。 次を使用して、モデル全体の各 DateTime 列の FormatString プロパティを同じように更新できます。

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 を使用してモデルを更新するには、まず 、セマンティック モデルの設定 または Power BI REST API を使用して、データ ソースの資格情報を設定する必要があります。

モデルの作成と複製

C# で記述されたコードを使用して Power BI モデルを作成および複製する必要があるとします。 まず、次のように、新しい Database オブジェクトを作成する CreateDatabase という名前の再利用可能な関数を記述します。

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;

}

この例では、まず Databases コレクション オブジェクトの GetNewNameメソッドを使用して、新しいモデル名がターゲット ワークスペース内で一意であることを確認します。 その後、次のコードに示すように、C# new 演算子を使用して Database オブジェクトとその Model オブジェクトを作成できます。 最後に、このメソッドは新しい Database オブジェクトを Databases コレクションに追加し、データベースを呼び出します 。Update メソッド。

新しいモデルを作成する代わりに既存のモデルをコピーすることが目的の場合は、次の CopyDatabase メソッドを使用して、新しい空のモデルを作成し、ソース モデルの Model オブジェクトで CopyTo を呼び出して、データ モデル全体を新しく作成されたモデルにコピーすることで、Power BI モデルを複製できます。

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 を使用して実際のデータ モデルを作成する必要がある場合を想像してみてください。 定義された列を含む新しいテーブルを作成し、3 レベルの次元階層を追加し、基になるテーブル クエリに 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 は、モデル内のテーブル間のリレーションシップを定義できる、Model オブジェクトの Relationships コレクションを公開します。 Products テーブルと Sales テーブルの間に一対多リレーションシップを確立する SingleColumnRelationship オブジェクトを作成するために必要なコードを次に示します。

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 サービスのモデル レポート。

重要

モデルを更新する前に、セマンティック モデルの設定または Power BI REST API を使用してデータ ソースの資格情報を指定する必要があります。

サンプル プロジェクト

この記事で見た C# コードを含むサンプル プロジェクトについては、 こちらを参照してください。 次に、TOM を使用したプログラミングを開始し、Power BI 用のカスタム ソリューションの開発でこの強力な新しい API を活用する方法を見つける必要があります。

こちらもご覧ください

XMLA エンドポイントとのセマンティック モデルの接続
XMLA エンドポイント接続のトラブルシューティング