Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In deze zelfstudie gebruikt u PowerShell om één REST API-aanvraag voor DAX-query's uitvoeren met meerdere EVALUATE instructies te verzenden en vervolgens het Apache Arrow-antwoord met meerdere resultaten te parseren. Met dit patroon kunt u meerdere gerelateerde resultsets in één round trip ophalen met behulp van een PowerShell-automatiseringsscript.
Waarom meerdere EVALUATE-instructies in één aanvraag indienen
De API VOOR DAX-query's uitvoeren accepteert één query tekenreeks die meerdere EVALUATE instructies kan bevatten. Elke instructie retourneert zijn eigen resultatenset en de antwoordbody is de concatenatie van één Arrow IPC-stream per EVALUATE instructie, in volgorde van declaratie. Het indienen van gerelateerde query's voorkomt de overhead per aanvraag van afzonderlijke HTTP-aanroepen, waaronder extra Microsoft Entra tokenvalidatie en initialisatie van DAX-engine. Het verzenden van meerdere EVALUATE statements in één verzoek kan ook helpen om de gevolgen van snelheidsbeperking van verzoeken te beperken. Power BI beperkt aanroepers per gebruiker tot 120 queryaanroepen per minuut voor querybewerkingen voor semantische modellen.
Wat u bouwt
In één PowerShell-script gaat u het volgende doen:
- Een Microsoft Entra toegangstoken verkrijgen.
- Bouw een aanvraagtekst met daarin
querydie drieEVALUATEstatements bevat. - Verzend het verzoek en registreer de onbewerkte Arrow IPC-responsstroom.
- Parseer het antwoord in één resultaatset per
EVALUATE-instructie. - Geef elke resultatenset weer als PowerShell-objecten.
Prerequisites
- PowerShell 7.4 of hoger. Windows PowerShell 5.1 wordt niet ondersteund omdat het
Apache.Arrowpakket dat in deze zelfstudie wordt gebruikt, conflicteert met deSystem.Memoryassembly die is opgenomen in PowerShell 5.1. - Een Power BI werkruimte in Premium- of Fabric-capaciteit met ten minste één semantisch model.
- Bouw en leesrechten voor het semantische model.
- De MicrosoftPowerBIMgmt-module voor verificatie. De cmdlets maken gebruik van Microsoft eigen Power BI client-app, zodat u uw eigen app niet hoeft te registreren in Microsoft Entra.
- De Apache.Arrow- en Apache.Arrow.Compression-.NET-bibliotheken gebruiken om het antwoord te deserialiseren. De REST API 'DAX-query's uitvoeren' comprimeert Arrow-buffers met LZ4-framecompressie, dus
Apache.Arrow.Compressionen de afhankelijkheden (K4os.Compression.LZ4,K4os.Compression.LZ4.Streams,K4os.Hash.xxHash,ZstdSharp.Port) zijn vereist. In de volgende stap ziet u hoe u ze downloadt. - De volgende tenantinstellingen zijn ingeschakeld in de Power BI-beheerportal:
- REST API voor query’s uitvoeren op gegevenssets (onder Ontwikkelaarsinstellingen).
- Sta XMLA-eindpunten toe en Analyseren in Excel met on-premises semantische modellen (onder Integratie-instellingen).
Installeer PowerShell 7.4 of hoger met behulp van winget:
winget install --id Microsoft.PowerShell --source winget
Start na de installatie de nieuwe shell met pwsh. Voer de resterende opdrachten uit in deze zelfstudie vanuit die sessie.
Installeer de MicrosoftPowerBIMgmt-module. De vlag -Force bevestigt de prompt voor de niet-vertrouwde PowerShell Gallery-opslagplaats.
Install-Module -Name MicrosoftPowerBIMgmt -Scope CurrentUser -Force
Download de vereiste NuGet-pakketten en pak hun assembly's uit naar C:\Tools\Apache.Arrow\. Een .nupkg bestand is een ZIP-archief, dus Expand-Archive werkt er rechtstreeks op. De lus selecteert in elk pakket de hoogst beschikbare netX.0-doelmap, zodat de assemblies compatibel blijven wanneer de pakketten nieuwere doelen uitbrengen.
$dest = "C:\Tools\Apache.Arrow"
New-Item -ItemType Directory -Force -Path $dest | Out-Null
$packages = @(
"Apache.Arrow",
"Apache.Arrow.Compression",
"K4os.Compression.LZ4",
"K4os.Compression.LZ4.Streams",
"K4os.Hash.xxHash",
"ZstdSharp.Port"
)
foreach ($pkg in $packages) {
$nupkg = Join-Path $env:TEMP "$pkg.nupkg"
$expand = Join-Path $env:TEMP $pkg
if (Test-Path $expand) { Remove-Item $expand -Recurse -Force }
Invoke-WebRequest -Uri "https://www.nuget.org/api/v2/package/$pkg" -OutFile $nupkg
Expand-Archive -Path $nupkg -DestinationPath $expand -Force
$libDirs = Get-ChildItem (Join-Path $expand "lib") -Directory
$best = $libDirs | Where-Object { $_.Name -match "^net\d" } |
Sort-Object Name -Descending | Select-Object -First 1
if (-not $best) {
$best = $libDirs | Sort-Object Name -Descending | Select-Object -First 1
}
Get-ChildItem (Join-Path $best.FullName "*.dll") |
Copy-Item -Destination $dest -Force
}
1 - Verifiëren
Meld u interactief aan bij de Power BI-service en extraheer vervolgens een toegangstoken. Voor de Connect-PowerBIServiceAccount cmdlet hoeft u uw eigen app niet te registreren in Microsoft Entra.
Connect-PowerBIServiceAccount -WarningAction SilentlyContinue
$accessToken = (Get-PowerBIAccessToken).Authorization -replace '^Bearer\s+',''
2 - Een verzoek maken met meerdere EVALUATE-instructies
Definieer de werkruimte- en semantische modeldoelen. Stel vervolgens de hoofdtekst van de aanvraag op. De query eigenschap is één tekenreeks die drie EVALUATE instructies bevat, gescheiden door lege regels.
$groupId = "YOUR_WORKSPACE_ID"
$datasetId = "YOUR_DATASET_ID"
$query = @"
EVALUATE
ROW("RowCount", COUNTROWS('Sales'))
EVALUATE
TOPN(10, 'Sales', 'Sales'[Amount], DESC)
EVALUATE
SUMMARIZECOLUMNS(
'Date'[Year],
"TotalSales", SUM('Sales'[Amount]))
"@
$body = @{
query = $query
resultsetRowcountLimit = 500000
} | ConvertTo-Json
3 - De aanvraag verzenden en de onbewerkte antwoordstroom vastleggen
Verzend de POST-aanvraag en lees de hoofdtekst van het antwoord als een binaire stroom. Gebruik HttpWebRequest in plaats van Invoke-RestMethod, Invoke-PowerBIRestMethodof Invoke-WebRequest. Het antwoord is een binaire Arrow IPC-gegevensstroom. De PowerShell-cmdlets op een hoger niveau interpreteren antwoordteksten als tekst, waardoor binaire inhoud wordt beschadigd.
HttpWebRequest geeft de ruwe stream ongewijzigd terug.
$url = "https://api.powerbi.com/v1.0/myorg/groups/$groupId" +
"/datasets/$datasetId/executeDaxQueries"
$request = [System.Net.HttpWebRequest]::Create($url)
$request.Method = "POST"
$request.ContentType = "application/json"
$request.Accept = "application/vnd.apache.arrow.stream"
$request.Timeout = 180000 # milliseconds
$request.Headers.Add("Authorization", "Bearer $accessToken")
$bodyBytes = [System.Text.Encoding]::UTF8.GetBytes($body)
$requestStream = $request.GetRequestStream()
$requestStream.Write($bodyBytes, 0, $bodyBytes.Length)
$requestStream.Close()
$response = $request.GetResponse()
$responseStream = $response.GetResponseStream()
# Buffer the response into memory so the parser can iterate over multiple Arrow IPC streams.
$memoryStream = New-Object System.IO.MemoryStream
$responseStream.CopyTo($memoryStream)
$responseStream.Close()
$response.Close()
$memoryStream.Position = 0
4 - Het antwoord met meerdere resultaatsets interpreteren
De antwoordtekst is de aaneenschakeling van één Apache Arrow IPC-stroom per EVALUATE statement. PowerShell wordt niet geleverd met een pijlparser, dus met deze stap wordt de Apache.Arrow .NET-bibliotheek geladen via een kleine inline C#-helper die is toegevoegd aan Add-Type. Door de stream-looplogica in C# te houden, blijft de oproepsite kort en wordt een lijst met resultatensets geretourneerd die door uw PowerShell-script kunnen worden herhaald. De helper opent een nieuwe ArrowStreamReader na elke einde-van-stroommarkering, zodat dezelfde lus een willekeurig aantal resultatensets in het antwoord afhandelt.
Add-Type -Path "C:\Tools\Apache.Arrow\Apache.Arrow.dll"
Add-Type -Path "C:\Tools\Apache.Arrow\Apache.Arrow.Compression.dll"
# Reference the full .NET reference set that ships with PowerShell 7 so the
# inline C# below can resolve BCL types such as List<T> and Dictionary<,>.
$refs = Get-ChildItem "$PSHOME\ref\*.dll" | ForEach-Object FullName
$refs += Get-ChildItem "C:\Tools\Apache.Arrow\*.dll" | ForEach-Object FullName
Add-Type -ReferencedAssemblies $refs -IgnoreWarnings -WarningAction SilentlyContinue -TypeDefinition @"
using System;
using System.Collections.Generic;
using System.IO;
using Apache.Arrow;
using Apache.Arrow.Compression;
using Apache.Arrow.Ipc;
public class DaxResultSet
{
public List<string> ColumnNames = new List<string>();
public List<Dictionary<string, object>> Rows =
new List<Dictionary<string, object>>();
}
public static class DaxMultiResultReader
{
public static List<DaxResultSet> ReadAll(Stream stream)
{
var results = new List<DaxResultSet>();
var codecFactory = new CompressionCodecFactory();
while (stream.Position < stream.Length)
{
var rs = new DaxResultSet();
bool gotSchema = false;
using (var reader = new ArrowStreamReader(stream, codecFactory, leaveOpen: true))
{
RecordBatch batch;
while ((batch = reader.ReadNextRecordBatch()) != null)
{
using (batch)
{
if (!gotSchema)
{
foreach (var f in batch.Schema.FieldsList)
rs.ColumnNames.Add(f.Name);
gotSchema = true;
}
for (int r = 0; r < batch.Length; r++)
{
var row = new Dictionary<string, object>();
for (int c = 0; c < batch.ColumnCount; c++)
row[rs.ColumnNames[c]] = GetValue(batch.Column(c), r);
rs.Rows.Add(row);
}
}
}
}
if (gotSchema) results.Add(rs);
}
return results;
}
private static object GetValue(IArrowArray a, int i)
{
if (a == null) return null;
if (a is DictionaryArray da)
{
// Resolve the dictionary index, then look up the value in the dictionary.
int dictIndex;
switch (da.Indices)
{
case Int32Array idx32: if (idx32.IsNull(i)) return null; dictIndex = idx32.GetValue(i).Value; break;
case Int16Array idx16: if (idx16.IsNull(i)) return null; dictIndex = idx16.GetValue(i).Value; break;
case Int8Array idx8: if (idx8.IsNull(i)) return null; dictIndex = idx8.GetValue(i).Value; break;
case Int64Array idx64: if (idx64.IsNull(i)) return null; dictIndex = (int)idx64.GetValue(i).Value; break;
default: return da.Indices.ToString();
}
return GetValue(da.Dictionary, dictIndex);
}
if (a is StringArray sa) return sa.GetString(i);
if (a is BooleanArray ba) return ba.IsNull(i) ? (object)null : ba.GetValue(i);
if (a is Int64Array i64) return i64.IsNull(i) ? (object)null : i64.GetValue(i);
if (a is Int32Array i32) return i32.IsNull(i) ? (object)null : i32.GetValue(i);
if (a is DoubleArray d) return d.IsNull(i) ? (object)null : d.GetValue(i);
if (a is Decimal128Array dec) return dec.GetValue(i);
if (a is Date32Array d32) return d32.GetDateTime(i);
if (a is Date64Array d64) return d64.GetDateTime(i);
if (a is TimestampArray ts) return ts.GetTimestamp(i);
return a.ToString();
}
}
"@
$results = [DaxMultiResultReader]::ReadAll($memoryStream)
Write-Host "Received $($results.Count) result sets."
5 - Werken met elke resultatenset
Converteer elke resultatenset naar PSCustomObject rijen. U kunt de rijen nu doorgeven aan Where-Object, Group-Object, Export-Csv of een andere PowerShell-cmdlet.
function ConvertTo-PSObjectRows {
param([Parameter(Mandatory)] $ResultSet)
foreach ($row in $ResultSet.Rows) {
$obj = [ordered]@{}
foreach ($col in $ResultSet.ColumnNames) { $obj[$col] = $row[$col] }
[PSCustomObject]$obj
}
}
$rowCount = ConvertTo-PSObjectRows -ResultSet $results[0]
$topProducts = ConvertTo-PSObjectRows -ResultSet $results[1]
$yearTotals = ConvertTo-PSObjectRows -ResultSet $results[2]
$rowCount | Format-Table
$topProducts | Format-Table
$yearTotals | Format-Table
Elke variabele bevat de rijen uit de bijbehorende EVALUATE instructie, in de volgorde waarin de instructies worden weergegeven in de aanvraag.
Troubleshooting
-
401 Niet geautoriseerd : het token in de cache is verlopen. Voer
Connect-PowerBIServiceAccountopnieuw uit om deze te vernieuwen en lees daarna$accessTokenopnieuw uitGet-PowerBIAccessToken. -
MSAL-waarschuwingen tijdens
Connect-PowerBIServiceAccount—MicrosoftPowerBIMgmtbundelt een oudere MSAL.NET die interne traceringsberichten verzendt (bijvoorbeeldSetAuthorityUriTryNormalizeRealm, )MsaDeviceOperationProvider is not availablebij de ernst van de waarschuwing. Ze zijn veilig te negeren zolang de cmdlet hetEnvironment/TenantId/UserNameblok afdrukt. Als u deze wilt onderdrukken, geeft u-WarningAction SilentlyContinueop. -
HTTP 200 met een foutresultatenset : de HTTP-aanvraag is geslaagd, maar de pijlstroom bevat een fout. Inspecteer de metagegevens van het schema voor
IsError=trueen leesFaultCode- enFaultString. Zie Best practices voor de REST API voor DAX-query's uitvoeren voor meer informatie. -
Invoke-RestMethodretourneert vervormde tekst : niet gebruikenInvoke-RestMethod,Invoke-PowerBIRestMethodofInvoke-WebRequestmet deze API. Het antwoord is binair; gebruikenHttpWebRequestzoals wordt weergegeven in stap 3. -
Add-Typekan niet worden geladenApache.Arrow.dll: bij Windows PowerShell 5.1 conflicteert hetApache.Arrowpakket met de in-boxSystem.Memoryassembly. Gebruik PowerShell 7.4 of hoger. -
Er worden geen of minder resultaatsets geretourneerd dan
EVALUATE-instructies — Controleer of elkeEVALUATE-instructie syntactisch geldig is. Eén ongeldigeEVALUATEzorgt ervoor dat de API een fout teruggeeft in plaats van een gedeeltelijke response met meerdere resultatensets.
Verwante inhoud
- Inzicht in de API voor het uitvoeren van DAX-query's
- Aan de slag met de REST API voor DAX-query's uitvoeren
- Tutorial: bouw een .NET middellaagservice met de REST API VOOR DAX-query's uitvoeren
- Zelfstudie: Extractie van grote volumes Python in Fabric notebooks
- Aanbevolen procedures voor de REST API voor DAX-query's uitvoeren
- REST API-verwijzing voor DAX-query's uitvoeren