Esercitazione: Creare un servizio .NET di livello intermedio con l'API REST per eseguire query DAX

In questa esercitazione si prende il Microsoft.Samples.XMLA.ExecuteQueries, un'API Web .NET che fa da proxy per le query DAX tramite l'endpoint XMLA usando ADOMD.NET, e modificarlo per utilizzare l'API REST Execute DAX Queries, che restituisce i risultati in formato IPC Apache Arrow. L'esempio fornisce il framework di livello intermedio (routing, limitazione della frequenza, probe di integrità). Questa esercitazione illustra come sostituire l'esecuzione di query XMLA/ADOMD con chiamate API REST e la gestione delle risposte Arrow IPC.

Prerequisites

  • .NET 8 SDK o versione successiva.
  • Un'area di lavoro Power BI in capacità Premium o Fabric con almeno un modello semantico.
  • Registrazione dell'app Microsoft Entra con un segreto del client.
  • L'entità principale del servizio aggiunta come membro con ruolo Collaboratore (o superiore) nell'area di lavoro.
  • Le impostazioni del tenant seguenti sono abilitate:
    • Esegui query del dataset API REST e Consenti ai principali di servizio di usare le API di Power BI (in Impostazioni sviluppatore).
    • Consenti endpoint XMLA e Analizza in Excel con modelli semantici locali (nelle Impostazioni di integrazione).

Per informazioni dettagliate sull'architettura del servizio di esempio, vedere sample README.

Prima di iniziare

Il servizio di esempio usa l'endpoint XMLA con ADOMD.NET. Questa esercitazione viene convertita per utilizzare l'API REST Execute DAX Queries, che restituisce i risultati in formato IPC Apache Arrow. Entrambi gli approcci consentono di eseguire query DAX su modelli semantici di Power BI, ma differiscono in modi importanti.

XMLA/ADOMD.NET Eseguire query DAX con l'API REST
Protocollo XMLA su HTTPS (binario proprietario) Standard REST (POST HTTP / risposta)
Libreria client Microsoft.AnalysisServices.AdomdClient - orientata ai Windows (pacchetto .NET Core disponibile ma supporto multipiattaforma limitato), gestisce sessioni e connessioni HttpClient + Apache.Arrow — lightweight, multipiattaforma, senza stato
Autenticazione Stringa di connessione con token di accesso; sessione a livello di connessione Token di accesso per richiesta; nessuno stato della sessione
Formato della risposta Set di righe tabulari analizzati dalla libreria client ADOMD Apache Arrow IPC: un formato binario a colonne con ampio supporto dell'ecosistema (Python, R, Spark, DuckDB)
Gestione delle connessioni Richiede il pooling per ammortizzare i costi di configurazione della sessione HTTP senza stato : nessun pooling necessario; MSAL gestisce la memorizzazione nella cache dei token
Migliore per Integrazioni legacy, query MDX, controllo granulare delle sessioni Nuovi servizi in cui si desidera un'integrazione HTTP più semplice, prestazioni basate su colonne o consumatori tra linguaggi

Scegli l'API REST Execute DAX Queries quando stai creando un nuovo servizio o i consumatori downstream possono beneficiare di Arrow IPC (ad esempio pipeline di analisi, notebook Python o dati a colonne). Mantenere XMLA/ADOMD se hai bisogno del supporto MDX o ti affidi a funzionalità specifiche della sessione, come i membri calcolati con ambito limitato a una singola sessione.

1 - Clonare e verificare l'esempio

Clonare il repository e confermarne la compilazione:

git clone https://github.com/dbrownems/Microsoft.Samples.XMLA.ExecuteQueries.git
cd Microsoft.Samples.XMLA.ExecuteQueries
dotnet build

La soluzione contiene due progetti: il servizio di livello intermedio (Microsoft.Samples.XMLA.ExecuteQueries) e un client di test di carico (Tester). Non è necessario eseguire il servizio originale in un'area di lavoro dinamica. Verificare che la compilazione abbia esito positivo prima di apportare modifiche.

2 - Aggiornare le NuGet dipendenze

Nel progetto Microsoft.Samples.XMLA.ExecuteQueries rimuovere il pacchetto ADOMD.NET e aggiungere pacchetti per l'API Arrow:

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

Mantenere il pacchetto Microsoft.PowerBI.Api se si desidera riutilizzare i relativi tipi di modello di richiesta/risposta; in caso contrario, rimuoverlo e definire oggetti DTO personalizzati.

3 - Sostituire il pool di connessioni ADOMD con la memorizzazione nella cache dei token MSAL

L'esempio utilizza AdomdConnectionPool.cs per gestire il pool di connessioni XMLA. L'API Arrow è un endpoint REST senza stato, quindi si sostituisce il pool di connessioni con la memorizzazione nella cache dei token MSAL.

Creare un nuovo file 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 memorizza nella cache automaticamente i token. Le chiamate successive restituiscono il token memorizzato nella cache fino alla scadenza.

Eliminare AdomdConnectionPool.cs e AdomdExtensions.cs. Non sono più necessari.

4 - Aggiornare il gestore di query per chiamare l'API Arrow

In Handlers.cs sostituire l'esecuzione di query ADOMD con una chiamata HTTP all'endpoint Esegui queries DAX.

Rimuovere tutti i riferimenti ADOMD (AdomdConnectionPool, AdomdConnection, AdomdCommand, WrappedConnection). Modificare le dipendenze che il gestore ha iniettato nei TokenService e HttpClient invece che nei pool di connessioni e nelle ricerche nell'area di lavoro.

Costruire l'URL dell'API REST utilizzando il workspace e i GUID del dataset già disponibili nei parametri del percorso.

var url = $"https://api.powerbi.com/v1.0/myorg/groups/{workspaceId}"
        + $"/datasets/{datasetId}/executeDaxQueries";

PUBBLICARE la query DAX con un corpo della richiesta JSON:

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();

Utilizzare HttpCompletionOption.ResponseHeadersRead per consentire che il corpo della risposta venga trasmesso in streaming senza buffering, un aspetto importante per set di risultati di grandi dimensioni.

5 - Gestire la risposta IPC Arrow

L'API di esecuzione delle query DAX restituisce uno o più flussi IPC Arrow concatenati nel corpo della risposta. Ogni flusso include metadati dello schema che ne indicano lo scopo:

  • Risultato dei dati : risultati della query (nessun flag di metadati speciale).
  • Errore nel risultato — nei metadati dello schema, con IsError=true e i valori FaultCode e FaultString.
  • Metriche di esecuzione : IsExecMetrics=true se sono state richieste metriche tramite il executionMetrics parametro .

Sostituire DataResult.cs con la logica che gestisce la risposta Arrow. Se il livello intermedio inoltra semplicemente Arrow IPC ai consumatori downstream, trasmetti i byte senza elaborazione.

context.Response.ContentType = "application/vnd.apache.arrow.stream";
await response.Content.CopyToAsync(context.Response.Body);

Se è necessario esaminare i risultati o convertire i formati, deserializzare il flusso Arrow con 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.
}

Controllare i metadati dello schema per rilevare le risposte agli errori:

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 - Semplificare la configurazione dell'area di lavoro

L'esempio appsettings.json configura gli endpoint XMLA e le ricerche dei nomi del set di dati perché ADOMD si connette in base al nome del catalogo. L'API REST Arrow usa i GUID dell'area di lavoro e del set di dati direttamente dall'URL della richiesta, quindi la configurazione è più semplice.

Aggiorna appsettings.json con le credenziali del principale del servizio e rimuovi i campi specifici di XMLA.

{
  "PowerBI": {
    "TenantId": "YOUR_TENANT_ID",
    "ClientId": "YOUR_APP_CLIENT_ID",
    "ClientSecret": "YOUR_CLIENT_SECRET"
  }
}

La Workspaces sezione con XmlaEndpoint e Datasets matrici non è più necessaria. È possibile eliminare Workspace.cs e Dataset.cso riconfigurare l'elenco come elenco di elementi consentiti per la Datasets governance (limitando i set di dati su cui il servizio può eseguire query).

7 - Registrare i servizi e aggiornare il routing

In Program.cs, sostituire le registrazioni del pool ADOMD e dell'area di lavoro con i nuovi servizi:

builder.Services.AddSingleton<TokenService>();
builder.Services.AddHttpClient();

Aggiornare la route in modo che corrisponda al pattern dell'endpoint delle query DAX di esecuzione:

app.MapPost(
    "/v1.0/myorg/groups/{workspaceId:Guid}"
    + "/datasets/{datasetId:Guid}/executeDaxQueries",
    Handlers.ExecuteDaxQueriesInGroup);

Il limite di frequenza esistente, la sonda di integrità e il contatore delle richieste dell'esempio rimangono utili così com'è.

8 - Testare il servizio

Eseguire il servizio:

dotnet run --project Microsoft.Samples.XMLA.ExecuteQueries

Da un altro terminale inviare una query 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'\'')"}'

La risposta è un flusso IPC Arrow binario. Salvarlo in un file ed esaminarlo con 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())
"

Riepilogo delle modifiche

File originale Action
AdomdConnectionPool.cs Eliminazione : sostituita dalla memorizzazione nella cache dei token MSAL in TokenService.cs
AdomdExtensions.cs Eliminazione : la logica di streaming JSON non è più necessaria
DataResult.cs Riscrivere — trasmettere Arrow IPC tramite, o deserializzare con ArrowStreamReader
Handlers.cs Riscrittura : HTTP POST per eseguire l'API query DAX invece dell'esecuzione ADOMD
Workspace.cs / Dataset.cs Semplificare o eliminare : l'API REST usa GUID, non i nomi di catalogo
Program.cs Aggiornamento — registrareTokenService e IHttpClientFactory; aggiornare percorso
appsettings.json Semplificare — solo le credenziali dell'oggetto servizio, rimuovere la configurazione XMLA
.csproj Update - rimuovere il pacchetto ADOMD; aggiungere Apache.Arrow e Microsoft.Identity.Client

Pulire le risorse

Al termine dei test:

  1. Arrestare il servizio locale (premere CTRL+C nel terminale).
  2. Se è stata creata una registrazione dell'app Microsoft Entra esclusivamente per questa esercitazione, passare al portale di Azure ed eliminarla.
  3. Rimuovere l'entità servizio dall'area di lavoro Power BI se non è più necessaria.