テーブル オブジェクト モデル (TOM) を使用したデータセットPower BIプログラミング

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

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

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

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

Diagram of application to dataset through the XMLA endpoint.

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

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

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

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

Link to workspace Settings.

注意

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

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

Workspace connection string in dataset Settings.

次の手順では、TOM を使用してプログラムする C# コードを記述する新しい .NET アプリケーションを作成します。 Web アプリケーションまたはデスクトップ アプリケーションは、.NET Framework上の .NET 5、.NET Core 3.1、またはそれ以前のバージョンを使用して作成できます。 この記事では、 .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.AdomdClient.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 datasets in workspace to display their names
    foreach (Database database in server.Databases) {
      Console.WriteLine(database.Name);
    }
  }
}

この例では、接続文字列にはワークスペース接続 URL が含まれていますが、ユーザーに関する情報は含まれています。 このコードでコンソール アプリケーションを実行すると、アプリケーションの実行が開始され、ブラウザーベースのウィンドウでログインするように求められます。 ワークスペース接続 URL によって参照されるワークスペースにアクセスするためのアクセス許可を持つユーザー アカウントでログインすると、TOM ライブラリはアクセス トークンを取得し、Power BI サービスに接続し、ワークスペース内のデータセットを列挙できます。

XMLA エンドポイントを介した接続の詳細については、「XMLA エンドポイントとのデータセット接続 - プレミアム ワークスペースへの接続」を参照してください。

ユーザー名とパスワードを使用した認証

セキュリティが重要でない開発とテストのシナリオでは、ユーザー名とパスワードをハードコーディングできるため、次のコードに示すように、プログラムを実行してコードをテストするたびに対話形式でログインする必要がなくなります。

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 とアプリケーション シークレットを使用してAzure AD アプリケーションを作成した場合は、次のコード サンプルを使用して、Azure AD アプリケーションのサービス プリンシパルとして実行するコードを認証できます。

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 コンテンツをサービス プリンシパルとアプリケーション シークレットに埋め込む」を参照してください。

Azure AD アクセス トークンを使用した認証

TOM は、有効なAzure AD アクセス トークンを使用して接続を確立する際にも柔軟性を提供します。 Azure ADを使用して認証フローを実装し、アクセス トークンを取得する開発者スキルがある場合は、次のコード サンプルに示すように、ユーザー名なしで 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 では、Azure AD リソース 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 のオブジェクト モデルは、データベース オブジェクトのコレクションを含む最上位の Server オブジェクトを持つ階層に基づいています。 Power BIで TOM を使用してプログラミングする場合、Server オブジェクトはPower BI ワークスペースを表し、Database オブジェクトはPower BI データセットを表します。

Tabular object model diagram with all objects

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

Hello World コードに示すように、server.Connect を呼び出すと、次のコードに示すように、Server オブジェクトの Databases コレクションを列挙することで、Power BI ワークスペース内に存在するデータセットを簡単に検出できます。

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

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

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

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

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

Tabular object model diagram with Table, partition, column, measure, and hierarchy

データベースModel オブジェクトを取得したら、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 を使用したデータセットの変更

上記のセクションでは、データベース オブジェクトとその Model オブジェクトにアクセスして、Power BI サービスで実行されているデータセットのデータ モデルを検査する方法について説明しました。 次は、テーブルにメジャーを追加して、TOM を使用して最初のデータセットの更新をプログラムします。

使用している容量は、 XMLA の読み取り/書き込みに対して有効にする必要があります。 既定では、XMLA エンドポイントのアクセス許可設定は [読み取り] に設定されているため、容量管理者のアクセス許可を持つユーザーが明示的に [読み取り書き込み] に設定する必要があります。 この設定は、管理ポータル[容量設定] ページで表示および更新できます。

XMLA Read Write setting in the Admin portal.

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# の新しい演算子を使用して新しい Measure オブジェクトを作成し、NameExpressionFormatString の値を指定します。 次に、Add メソッドを呼び出して、ターゲット Table オブジェクトの Measures コレクションに新しい Measure オブジェクトを追加します。 最後に、Model オブジェクトの SaveChanges メソッドを呼び出して、変更を Power BI Service のデータセットに書き戻します。

データセットの更新は、 SaveChanges を呼び出すまでメモリにバッチ処理されます。 Imagineテーブル内のすべての列を非表示にするシナリオです。 まず、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 の機能は、一般的で包括的な方法で更新を実行する機会を提供します。 Imagine、DateTime データ型に基づいて多数のテーブルと 10 または数百の列を含むデータセットを管理するシナリオです。 データセット全体の各 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 エンドポイント経由で表示されます。

Refresh history dialog

注意

TOM は更新操作を開始する機能を提供しますが、Power BI データセットのデータ ソース資格情報を設定することはできません。 TOM を使用してデータセットを更新するには、まずデータセット設定でデータ ソースの資格情報を設定するか、Power BI REST API を使用する必要があります。

データセットの作成と複製

Imagine C# で記述されたコードを使用して、データセットPower BI作成して複製する必要があります。 まず、 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;

}

この例では、まず Databases コレクション オブジェクトの GetNewNamemethod を使用して、新しいデータセット名がターゲット ワークスペース内で一意であることを確認します。 その後、次のコードに示すように、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 オブジェクトにリレーションシップ コレクションを公開します。 Products テーブルと Sales テーブルの間に 1 対多のリレーションシップを確立する 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 サービスで作成された新しいデータセットを確認し、それを使用して新しいレポートを作成できるようになります。

Dataset report in the Power BI service.

重要

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

サンプル プロジェクト

この記事で見た C# コードを含むサンプル プロジェクトは 、ここで入手できます。 次に、TOM を使用したプログラミングを開始し、この強力な新しい API をPower BI用のカスタム ソリューションの開発に活用する方法を見つけ出します。

関連項目

XMLA エンドポイントを使用したデータセット接続
XMLA エンドポイント接続のトラブルシューティング