このチュートリアルでは、Microsoft.Samples.XMLA.ExecuteQueries サンプル、つまり ADOMD.NET を使用して XMLA エンドポイントを介して DAX クエリをプロキシする .NET Web API を取り上げ、これを Execute DAX Queries REST API を使うように変更します。この変更により、結果が Apache Arrow IPC 形式で返されます。 このサンプルでは、中間層フレームワーク (ルーティング、レート制限、正常性プローブ) を提供します。 このチュートリアルでは、XMLA/ADOMD クエリ実行プラミングを REST API 呼び出しと方向 IPC 応答処理に置き換える方法について説明します。
[前提条件]
- .NET 8 SDK 以降。
- 少なくとも 1 つのセマンティック モデルを持つ Premium またはFabric容量のPower BI ワークスペース。
- クライアント シークレットを使用したMicrosoft Entra アプリの登録。
- サービス プリンシパルは、ワークスペース メンバーとして共同作成者(またはそれ以上)のロールで追加されました。
- 次のテナント設定が有効になっています。
- データセット実行クエリ REST API および サービス プリンシパルが Power BI API を使用 (開発者設定)。
- オンプレミスのセマンティック モデルで XMLA エンドポイントと Excelでの分析を許可 (統合設定)。
サンプル サービス アーキテクチャの詳細については、sample README を参照してください。
始める前の準備
サンプル サービスでは、ADOMD.NET で XMLA エンドポイントを使用します。 このチュートリアルでは、DAX クエリの実行 REST API を使用するように変換します。これにより、結果が Apache Arrow IPC 形式で返されます。 どちらの方法でも、Power BIセマンティック モデルに対して DAX クエリを実行できますが、重要な方法は異なります。
| XMLA/ADOMD.NET | DAX クエリの実行 REST API | |
|---|---|---|
| プロトコル | HTTPS 経由の XMLA (独自のバイナリ) | Standard REST (HTTP POST/response) |
| クライアント ライブラリ |
Microsoft.AnalysisServices.AdomdClient — Windows指向 (.NET Core パッケージは使用可能ですが、クロスプラットフォーム サポートは制限されています)、セッションと接続を管理します |
HttpClient
+
Apache.Arrow — 軽量、クロスプラットフォーム、ステートレス |
| 認証 | アクセス トークンを含む接続文字列。接続レベルのセッション | リクエストごとのベアラートークンで、セッション状態なし |
| 応答の形式 | ADOMD クライアント ライブラリによって解析された表形式の行セット | Apache Arrow IPC — 広範なエコシステムサポート (Python、R、Spark、DuckDB) を備えた列形式バイナリ形式 |
| 接続管理 | セッションセットアップコストを分散するためにプールが必要 | ステートレス HTTP — プーリングは必要ありません。MSAL がトークン キャッシュを処理する |
| 最適な用途 | 従来の統合、MDX クエリ、きめ細かなセッション制御 | よりシンプルな HTTP 統合、列形式のパフォーマンス、または言語間コンシューマーが必要な新しいサービス |
新しいサービスを構築するとき、またはダウンストリーム コンシューマーが Arrow IPC (分析パイプライン、Python ノートブック、列形式データベースなど) の恩恵を受けることができる場合は、
1 - サンプルを複製して確認する
リポジトリを複製し、コンパイルを確認します。
git clone https://github.com/dbrownems/Microsoft.Samples.XMLA.ExecuteQueries.git
cd Microsoft.Samples.XMLA.ExecuteQueries
dotnet build
このソリューションには、中間層サービス (Microsoft.Samples.XMLA.ExecuteQueries) とロード テスト クライアント (Tester) の 2 つのプロジェクトが含まれています。 ライブ ワークスペースに対して元のサービスを実行する必要はありません。変更を行う前に、ビルドが成功したことを確認するだけです。
2 - NuGet の依存関係を更新する
Microsoft.Samples.XMLA.ExecuteQueries プロジェクトで、ADOMD.NET パッケージを削除し、Arrow API のパッケージを追加します。
cd Microsoft.Samples.XMLA.ExecuteQueries
dotnet remove package Microsoft.AnalysisServices.AdomdClient.NetCore.retail.amd64
dotnet add package Apache.Arrow
dotnet add package Microsoft.Identity.Client
要求/応答モデルの種類を再利用する場合は、Microsoft.PowerBI.Api パッケージを保持します。それ以外の場合は削除し、独自の DTO を定義します。
3 - ADOMD 接続プールを MSAL トークン キャッシュに置き換える
このサンプルでは、AdomdConnectionPool.cs を使用して XMLA 接続をプールします。 Arrow API はステートレス REST エンドポイントであるため、接続プールを MSAL トークン キャッシュに置き換えます。
新しい TokenService.cs ファイルを作成します。
using Microsoft.Identity.Client;
public class TokenService
{
private readonly IConfidentialClientApplication _app;
private readonly string[] _scopes =
{ "https://analysis.windows.net/powerbi/api/.default" };
public TokenService(IConfiguration config)
{
_app = ConfidentialClientApplicationBuilder
.Create(config["PowerBI:ClientId"])
.WithClientSecret(config["PowerBI:ClientSecret"])
.WithAuthority(AzureCloudInstance.AzurePublic,
config["PowerBI:TenantId"])
.Build();
}
public async Task<string> GetAccessTokenAsync()
{
var result = await _app
.AcquireTokenForClient(_scopes).ExecuteAsync();
return result.AccessToken;
}
}
MSAL はトークンを自動的にキャッシュします。後続の呼び出しでは、有効期限が切れるまでキャッシュされたトークンが返されます。
AdomdConnectionPool.csとAdomdExtensions.csを削除します。 これらは不要です。
4 - クエリ ハンドラーを更新して Arrow API を呼び出す
Handlers.csで、ADOMD クエリの実行を、DAX クエリの実行エンドポイントへの HTTP 呼び出しに置き換えます。
すべての ADOMD 参照 (AdomdConnectionPool、 AdomdConnection、 AdomdCommand、 WrappedConnection) を削除します。 接続プールとワークスペースの参照ではなく、ハンドラーの挿入された依存関係を TokenService と HttpClient に変更します。
ルート パラメーターで既に使用可能なワークスペースとデータセット GUID から REST API URL を構築します。
var url = $"https://api.powerbi.com/v1.0/myorg/groups/{workspaceId}"
+ $"/datasets/{datasetId}/executeDaxQueries";
JSON 要求本文を使用して DAX クエリを POST します。
var token = await tokenService.GetAccessTokenAsync();
using var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent(
JsonSerializer.Serialize(new { query, queryTimeout = 120 }),
Encoding.UTF8, "application/json");
var response = await httpClient.SendAsync(
request, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
HttpCompletionOption.ResponseHeadersReadを使用して、バッファリングなしで応答本文がストリームされるようにします。これは、大規模な結果セットにとって重要です。
5 - 矢印 IPC 応答を処理する
DAX クエリの実行 API は、応答本文で連結された 1 つ以上の方向 IPC ストリームを返します。 各ストリームには、その目的を示すスキーマ メタデータが含まれています。
- データの結果 — クエリ結果 (特別なメタデータ フラグなし)。
-
エラー結果 —
IsError=trueはスキーマメタデータ内にあり、FaultCode値とFaultString値を使用しています。 -
実行メトリック —
IsExecMetrics=true(executionMetricsパラメーターを使用してメトリックを要求した場合)。
DataResult.csを、矢印応答を処理するロジックに置き換えます。 中間層が単に Arrow IPC をダウンストリーム コンシューマーに転送する場合は、逆シリアル化せずにバイトをストリーミングします。
context.Response.ContentType = "application/vnd.apache.arrow.stream";
await response.Content.CopyToAsync(context.Response.Body);
結果を検査したり、形式を変換したりする必要がある場合は、 ArrowStreamReaderを使用して方向ストリームを逆シリアル化します。
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new ArrowStreamReader(stream);
while (true)
{
var batch = await reader.ReadNextRecordBatchAsync();
if (batch == null) break;
// Process batch — convert to JSON, filter rows, etc.
}
エラー応答を検出するには、スキーマ メタデータを確認します。
var metadata = reader.Schema.Metadata;
if (metadata.TryGetValue("IsError", out var isError)
&& isError == "true")
{
var faultCode = metadata.GetValueOrDefault(
"FaultCode", "Unknown");
var faultString = metadata.GetValueOrDefault(
"FaultString", "Unknown error");
// Return error to caller
}
6 - ワークスペースの構成を簡略化する
サンプルの appsettings.json では、ADOMD がカタログ名で接続するため、XMLA エンドポイントとデータセット名の参照が構成されます。 Arrow REST API では、要求 URL から直接ワークスペースとデータセット GUID が使用されるため、構成が簡単になります。
サービス プリンシパルの資格情報で appsettings.json を更新し、XMLA 固有のフィールドを削除します。
{
"PowerBI": {
"TenantId": "YOUR_TENANT_ID",
"ClientId": "YOUR_APP_CLIENT_ID",
"ClientSecret": "YOUR_CLIENT_SECRET"
}
}
Workspaces配列とXmlaEndpoint配列を含む Datasets セクションは不要です。
Workspace.csとDataset.csを削除したり、ガバナンスの許可リストとしてDatasetsリストを再利用したりできます (サービスがクエリできるデータセットを制限します)。
7 - サービスを登録し、ルーティングを更新する
Program.csで、ADOMD プールとワークスペースの登録を新しいサービスに置き換えます。
builder.Services.AddSingleton<TokenService>();
builder.Services.AddHttpClient();
DAX クエリの実行エンドポイント パターンに一致するようにルートを更新します。
app.MapPost(
"/v1.0/myorg/groups/{workspaceId:Guid}"
+ "/datasets/{datasetId:Guid}/executeDaxQueries",
Handlers.ExecuteDaxQueriesInGroup);
サンプルに含まれる既存のレートリミッター、正常性プローブ、そしてリクエストカウンターは、現状のままで引き続き役立ちます。
8 - サービスをテストする
サービスを実行します。
dotnet run --project Microsoft.Samples.XMLA.ExecuteQueries
別のターミナルから、DAX クエリを送信します。
curl -X POST https://localhost:3000/v1.0/myorg/groups/YOUR_WORKSPACE_ID/datasets/YOUR_DATASET_ID/executeDaxQueries \
-H "Content-Type: application/json" \
-d '{"query": "EVALUATE TOPN(5, '\''DimProduct'\'')"}'
応答はバイナリの Arrow IPC ストリームです。 ファイルに保存し、Pythonで検査します。
curl -s -o result.arrow https://localhost:3000/v1.0/myorg/groups/YOUR_WORKSPACE_ID/datasets/YOUR_DATASET_ID/executeDaxQueries \
-H "Content-Type: application/json" \
-d '{"query": "EVALUATE TOPN(5, '\''DimProduct'\'')"}'
python -c "
import pyarrow as pa
reader = pa.ipc.open_stream('result.arrow')
table = reader.read_all()
print(table.schema)
print(table.to_pandas())
"
変更の概要
| 元のファイル | アクション |
|---|---|
AdomdConnectionPool.cs |
Delete — MSAL トークン キャッシュによって置き換えられます TokenService.cs |
AdomdExtensions.cs |
削除 - JSON ストリーミング ロジックが不要になった |
DataResult.cs |
書き換え — Arrow IPCをストリーム配信するか、ArrowStreamReaderでデシリアライズする |
Handlers.cs |
書き換え — ADOMD 実行の代わりに DAX クエリ API を実行するための HTTP POST |
Workspace.cs / Dataset.cs |
簡略化または削除 - REST API では、カタログ名ではなく GUID が使用されます |
Program.cs |
更新 — TokenService と IHttpClientFactoryの登録; ルートの更新 |
appsettings.json |
簡略化 — サービス プリンシパルの資格情報のみ。XMLA 構成の削除 |
.csproj |
Update — ADOMD パッケージを削除します。Apache.Arrow とMicrosoft.Identity.Client を追加します。 |
リソースをクリーンアップする
テストが完了したら、次の手順を実行します。
- ローカル サービスを停止します (ターミナルで Ctrl キーを押しながら C キーを押します)。
- このチュートリアル専用のMicrosoft Entra アプリ登録を作成した場合は、Azure ポータルに移動して削除します。
- 不要になった場合は、Power BI ワークスペースからサービス プリンシパルを削除します。