Tutorial: Construir um serviço de camada intermediária .NET com a API REST para Executar Consultas DAX

Neste tutorial, você utilizará o exemplo Microsoft.Samples.XMLA.ExecuteQueries — uma API Web .NET que faz proxy de consultas DAX através do endpoint XMLA usando ADOMD.NET — e irá modificá-lo para usar a API REST Execute DAX Queries, que retorna resultados no formato Apache Arrow IPC. O exemplo fornece a estrutura de camada intermediária (roteamento, limitação de taxa, investigação de integridade). Este tutorial mostra como substituir a infraestrutura de execução de consultas XMLA/ADOMD por chamadas à API REST e tratamento de resposta Arrow IPC.

Pré-requisitos

  • .NET SDK 8 ou posterior.
  • Um workspace Power BI em capacidade Premium ou Fabric com pelo menos um modelo semântico.
  • Um registro de aplicativo do Microsoft Entra com um segredo de cliente.
  • A entidade de serviço adicionada como um membro do workspace com a função Colaborador (ou superior).
  • As seguintes configurações de locatário estão habilitadas:
    • API REST para Execução de Consultas em Conjuntos de Dados e Permitir que os principais de serviço usem as APIs do Power BI (em Configurações do desenvolvedor).
    • Permitir endpoints XMLA e Analisar no Excel com modelos semânticos locais (nas Configurações de Integração).

Para obter detalhes sobre a arquitetura de serviço de exemplo, consulte o sample README.

Antes de começar

O serviço de exemplo usa o ponto de extremidade XMLA com ADOMD.NET. Este tutorial converte para usar a API REST para Executar Consultas DAX, que retorna resultados no formato IPC do Apache Arrow. Ambas as abordagens permitem que você execute consultas DAX em modelos semânticos do Power BI, mas elas diferem de maneiras importantes.

XMLA/ADOMD.NET Executar consultas DAX na API REST
Protocolo XMLA sobre HTTPS (binário proprietário) Padrão REST (HTTP POST/resposta)
Biblioteca de clientes Microsoft.AnalysisServices.AdomdClient — orientado para Windows (pacote .NET Core disponível, mas com suporte multiplataforma limitado), gerencia sessões e conexões HttpClient + Apache.Arrow — leve, multiplataforma, sem estado
Autenticação Cadeia de conexão com token de acesso; sessão no nível da conexão Token de portador por solicitação; nenhum estado de sessão
Formato da resposta Conjuntos de linhas tabulares analisados pela biblioteca de clientes do ADOMD IPC do Apache Arrow — um formato binário columnar com amplo suporte ao ecossistema (Python, R, Spark, DuckDB)
Gerenciamento de conexões Requer agrupamento para amortizar o custo de configuração da sessão HTTP sem estado — sem necessidade de pool; A MSAL manipula o cache de token
Mais adequado para Integrações herdadas, consultas MDX, controle de sessão refinado Novos serviços em que você deseja uma integração HTTP mais simples, desempenho colunar ou consumidores entre idiomas

Escolha a Execute DAX Queries REST API ao criar um novo serviço, ou quando seus consumidores downstream puderem se beneficiar do Arrow IPC (por exemplo, pipelines de análise, notebooks Python ou bancos de dados columnares). Mantenha XMLA/ADOMD se precisar de suporte MDX ou depender de funcionalidades de nível de sessão, como membros calculados com escopo restrito a uma sessão.

1 – Clonar e verificar o exemplo

Clone o repositório e confirme que ele é compilado:

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

A solução contém dois projetos: o serviço de camada intermediária (Microsoft.Samples.XMLA.ExecuteQueries) e um cliente de teste de carga (Tester). Você não precisa executar o serviço original em um workspace ao vivo. Basta verificar se o build foi bem-sucedido antes de fazer alterações.

2 – Atualizar dependências do NuGet

No projeto Microsoft.Samples.XMLA.ExecuteQueries, remova o pacote ADOMD.NET e adicione pacotes para a API de Seta:

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

Mantenha o pacote Microsoft.PowerBI.Api se quiser reutilizar seus tipos de modelo de solicitação/resposta; caso contrário, remova-o e defina seus próprios DTOs.

3 – Substituir o pool de conexões do ADOMD pelo cache de token MSAL

O exemplo usa AdomdConnectionPool.cs para agrupar conexões XMLA. A API Arrow é um ponto de extremidade REST sem estado, portanto, você substitui o pool de conexões pelo cache de tokens MSAL.

Criar um novo arquivo 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;
    }
}

A MSAL armazena tokens em cache automaticamente — chamadas subsequentes retornam o token armazenado em cache até que ele expire.

Excluir AdomdConnectionPool.cs e AdomdExtensions.cs. Eles não são mais necessários.

4 – Atualizar o manipulador de consulta para chamar a API Arrow

Em Handlers.cs, substitua a execução da consulta do ADOMD por uma chamada HTTP para o endpoint Execute DAX Queries.

Remover todas as referências do ADOMD (AdomdConnectionPool, , AdomdConnection, AdomdCommand, WrappedConnection). Altere as dependências injetadas do manipulador para TokenService e HttpClient em vez de pools de conexão e consultas de espaço de trabalho.

Crie a URL da API REST com base nos GUIDs do workspace e do conjunto de dados já disponíveis nos parâmetros de rota:

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

POSTE a consulta DAX com um corpo de solicitação 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();

Use HttpCompletionOption.ResponseHeadersRead para que o corpo da resposta transmita sem buffer – isso é importante para grandes conjuntos de resultados.

5 – Tratar a resposta do IPC do Arrow

A API Execute Consultas DAX retorna um ou mais fluxos de IPC Arrow concatenados no corpo da resposta. Cada fluxo inclui metadados de esquema que indicam sua finalidade:

  • Resultado dos dados – os resultados da consulta (sem sinalizadores de metadados especiais).
  • Resultado do erroIsError=true nos metadados do esquema, com FaultCode e FaultString nos valores.
  • Métricas de execuçãoIsExecMetrics=true (se você solicitou métricas por meio do executionMetrics parâmetro).

Substitua DataResult.cs pela lógica que manipula a resposta do Arrow. Se a camada intermediária simplesmente encaminha o IPC do Arrow para consumidores a jusante, encaminhe os bytes sem desserialização.

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

Se você precisar inspecionar resultados ou converter formatos, desserializar o fluxo Arrow com 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.
}

Verifique os metadados de esquema para detectar respostas de erro:

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 – Simplificar a configuração do workspace

O exemplo configura extremidades XMLA e pesquisas de nomes de conjuntos de dados porque o ADOMD se conecta pelo nome do catálogo. A Arrow API REST utiliza GUIDs de workspace e conjunto de dados diretamente da URL de solicitação, tornando a configuração mais simples.

Atualize appsettings.json com suas credenciais de entidade de serviço e remova os campos específicos do XMLA:

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

A Workspaces seção com XmlaEndpoint e Datasets matrizes não é mais necessária. Você pode excluir Workspace.cs e Dataset.cs, ou reutilizar a Datasets lista como uma lista de permissões para governança (restringindo quais conjuntos de dados o serviço pode consultar).

7 – Registrar serviços e atualizar o roteamento

Em Program.cs, substitua o pool do ADOMD e os registros de workspace pelos novos serviços:

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

Atualize a rota para corresponder ao padrão do endpoint Executar Consultas DAX.

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

O limitador de taxa existente, a sonda de integridade e o contador de solicitações do exemplo permanecem úteis como estão.

8 – Testar o serviço

Execute o serviço:

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

Em outro terminal, envie uma consulta 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'\'')"}'

A resposta é um fluxo de IPC Arrow binário. Salve-o em um arquivo e inspecione com 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())
"

Resumo das alterações

Arquivo original Ação
AdomdConnectionPool.cs Excluir – substituído pelo cache de token MSAL em TokenService.cs
AdomdExtensions.cs Excluir — a lógica de streaming JSON não é mais necessária
DataResult.cs Reescrever – transmitir Arrow IPC ou desserializar com ArrowStreamReader
Handlers.cs Reescrever – HTTP POST para executar consultas DAX na API em vez da execução do ADOMD
Workspace.cs / Dataset.cs Simplificar ou excluir — a API REST usa GUIDs, não nomes de catálogo
Program.cs Atualizar – registrar TokenService e IHttpClientFactory; atualizar rota
appsettings.json Simplificar — somente credenciais de entidade de serviço; remover configuração XMLA
.csproj Update — remova o pacote do ADOMD; adicionar Apache.Arrow e Microsoft.Identity.Client

Limpar os recursos

Quando terminar de testar:

  1. Interrompa o serviço local (pressione Ctrl+C no terminal).
  2. Se você criou um registro de aplicativo Microsoft Entra somente para este tutorial, navegue até o portal Azure e exclua-o.
  3. Remova a entidade de serviço do espaço de trabalho do Power BI se ela não for mais necessária.