mcpField
C
connect-pbid

connect-pbid

github.com/data-goblin数据与数据库1.1k70 次安装今天更新
代码仓库

关于 connect-pbid

connect-pbid 是一项通过 PowerShell、TOM 和 ADOMD.NET 操作 Power BI Desktop 模型的技能。在 Tabular Editor CLI 或 Power BI MCP 服务不可用时,可用于检查本地模型、运行 DAX 查询、刷新和修改模型。

主要功能

  1. 01连接本地 Analysis Services 模型
  2. 02枚举模型元数据
  3. 03运行 DAX 查询和跟踪
  4. 04刷新和修改模型

适用场景

  1. 01检查 Power BI Desktop 中打开的模型
  2. 02用 DAX 查询本地模型数据
  3. 03首选工具不可用时更新模型元数据

MCP 相关

此技能不需要 MCP 服务,适用于 Tabular Editor CLI 或 Power BI MCP 服务不可用时。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/data-goblin/power-bi-agentic-development --skill connect-pbid

安装后在 Claude Code 中输入/connect-pbid确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

daxlib.sh
references/
annotations.md
calendar-column-groups.md
dax-expressions.md
dax-pitfalls.md
daxlib.md
desktop-bridge.md
evaluateandlog-debugging.md
export-model.md
load-tmdl-files.md
parallels-macos.md
performance-profiling.md
query-listener.md
refresh-model.md
tom-object-types.md
vertipaq-stats.md
scripts/
connect-and-enumerate.ps1
connect-from-mac.sh
create-field-parameter.ps1
debug-dax.ps1
explore-model.ps1
load-tmdl.ps1
modify-tom-objects.ps1
query-dax.ps1
refresh-table.ps1
scripts/daxlib-tom/
.gitignore
daxlib-tom.csproj
Program.cs

详细介绍

连接到 Power BI Desktop(本地 Analysis Services)

重要: 使用此技能时遇到的错误、意外情况和特定于模型的细节,请记录在 .claude/rules/connect-pbid.md 中。此文件必须以“Learnings from Claude about connecting to semantic models via the connect-pbid skill”开头。只编写当前有效的参考笔记(例如:“QueryGroup 属性返回一个对象;使用 .Folder 获取名称字符串”);不要记录变更日志或事件历史。省略技能或其参考资料中已记录的内容。文件长度始终不得超过 1500 个字符;添加新内容时删除过时条目。不要过度关注此文件;仅在发现真正意外的情况时更新。

注意: 无需 MCP 服务。使用 PowerShell 和 TOM/ADOMD.NET 访问本地模型。 如果报告画布也在处理范围内,请将其与 pbir 搭配用于报告操作;切勿直接修改 报告 JSON。

关于如何通过 PowerShell 中的 Tabular Object Model(TOM)和 ADOMD.NET 连接到 Power BI Desktop 本地表格模型的专家指南。涵盖连接、枚举、DAX 查询、查询跟踪和完整模型修改。

何时使用此技能

仅当 Tabular Editor CLI 或 Power BI MCP 服务不可用时才启用。TOM 比直接编辑 TMDL 更可靠,因为它会针对引擎验证更改并以原子方式应用。

警告: 此技能不支持通过 XMLA 终结点访问远程模型。对于 Direct Lake 模型或托管在 Fabric 上的模型,请改用 Tabular Editor CLI 或 Power BI MCP 服务;本地 Analysis Services 代理不会向外部 TOM/ADOMD.NET 连接公开 Direct Lake 数据库。

模型和报告:路由

Power BI Desktop 将模型和报告作为两个独立的本地界面提供。此技能负责模型界面和报告画布验证,并将报告创作工作路由到相应技能:

  • 模型(表、列、度量值、关系、角色、计算组、刷新):使用此技能,通过本地 Analysis Services 实例上的 TOM/ADOMD 进行操作。编辑模型时,如果有可用的 te CLI 或模型 MCP,请优先使用;否则回退到此技能中的 TOM(参见“何时使用此技能”)。
  • 报告画布验证(编辑后重新加载、页面截图):使用此技能,通过原始 Desktop Bridge 命名管道 API(第 13 节)进行操作。
  • 报告创作(视觉对象、页面、格式设置、筛选器、书签、主题):使用 reports 插件中的 pbir-cli 技能(它调用 pbir CLI)。此处的 Desktop Bridge 仅用于重新加载和截屏;不会编辑视觉对象。所有视觉对象或页面更改都应交由 pbir-cli 处理。
  • 直接编辑报告 JSON(仅当 pbir 不可用时):使用 pbip 插件中的 pbir-format 技能。

处理已打开的 PBIP 时,请完整执行以下流程:在此处使用 TOM 更改模型,使用 pbir-cli 更改视觉对象,然后在此处使用 Desktop Bridge 重新加载并截图进行验证,之后继续迭代。

重要

  • 连接前必须打开 Power BI Desktop 并加载模型;如果出现错误,可能是因为这是连接到远程模型的“精简报表”,或者是 Direct Lake 模型(它使用会阻止外部连接的远程代理)
  • 本地 Analysis Services 实例只接受来自 localhost 的连接
  • 如果同时打开了多个 PBI Desktop 文件,就会有多个 msmdsrv.exe 进程使用不同端口。连接到每个端口,读取 $server.Databases[0].Name;如果找到多个模型,请询问用户要使用哪个模型。安装了 pbir CLI 后,优先使用 pbir desktop list 将每个 Desktop PID 映射到其打开的确切文件(参见第 2a 节)
  • 报告 Databases: 0 的工作区引擎属于精简报表(与远程模型实时连接);没有可连接的本地模型。应通过远程模型查询精简报表(pbir model -q 会自动路由到远程模型)
  • 通过 Bash 执行 PowerShell 命令时,始终使用 60000ms 或更长的超时时间
  • Shell 转义:Bash 会静默吞掉 PowerShell 的 $ 变量($env:TEMP、$server 等)。有两种方法:(1) 将 -Command 参数用单引号括起来,让 Bash 将 $ 原样传给 PowerShell;(2) 使用 heredoc 写入 .ps1 文件(单引号分隔符可保留 $),然后使用 -File。在 macOS 上通过 Parallels 执行时,prlctl -> cmd.exe -> powershell.exe 调用链会增加额外的转义层;对于复杂脚本,.ps1 文件更可靠,但简短命令使用带单引号的内联 -Command 即可。
  • 运行 PowerShell 命令或脚本时,始终使用 -ExecutionPolicy Bypass。Windows 默认会阻止未签名的脚本。
  • 脚本文件位置——持久脚本应放在项目对应的 Agent harness 脚本目录中(取决于 harness,可以是 .claude/scripts/、.github/scripts/、.cursor/scripts/、.gemini/scripts/ 等)。临时或一次性脚本应放在项目的 tmp/ 目录中(该目录应加入 .gitignored)。不要将脚本写入 ./ 根目录或 /tmp/。
  • 未经用户明确指示,不要修改模型元数据
  • 始终调用 $model.SaveChanges() 以保存修改;否则,修改会被丢弃
  • 对于在 Parallels 中运行 PBI Desktop 的 macOS 用户,请参阅 parallels-macos.md
  • 此插件已启用验证钩子;它们会验证 DAX 引用、强制执行度量值元数据、检查引用完整性,并报告兼容级别升级机会。可在 hooks/config.yaml 中切换检查项。

1. 前提条件

要求说明
Power BI Desktop已打开并加载模型(.pbix 或 .pbip)
PowerShell在运行 PBI Desktop 的计算机上可用
NuGet CLI用于安装软件包(winget install Microsoft.NuGet)
TOM NuGet PackageMicrosoft.AnalysisServices.retail.amd64——模型元数据
ADOMD.NET PackageMicrosoft.AnalysisServices.AdomdClient.retail.amd64——DAX 查询

仅在尚未安装时安装这两个软件包:

$pkgDir = "$env:TEMP\tom_nuget"
if (-not (Test-Path "$pkgDir\Microsoft.AnalysisServices.retail.amd64")) {
    nuget install Microsoft.AnalysisServices.retail.amd64 -OutputDirectory $pkgDir -ExcludeVersion
}
if (-not (Test-Path "$pkgDir\Microsoft.AnalysisServices.AdomdClient.retail.amd64")) {
    nuget install Microsoft.AnalysisServices.AdomdClient.retail.amd64 -OutputDirectory $pkgDir -ExcludeVersion
}

软件包会在 lib\net45\ 下安装 DLL。使用 Add-Type -Path 加载。

如果 TOM 操作因兼容级别错误或缺少类型而失败,.retail.amd64 软件包可能太旧。较新的软件包(Microsoft.AnalysisServices,.NET 8+)包含更新的 TOM 功能。有关软件包差异的详细信息,请参阅 daxlib.md。

2. 快速入门

在一个脚本中查找端口、加载 TOM、连接并枚举:

# Find ports (deduped; netstat lists IPv4 and IPv6 entries per port)
$pids = (Get-Process msmdsrv -ErrorAction SilentlyContinue).Id
$ports = netstat -ano | Select-String "LISTENING" |
    Where-Object { $pids -contains ($_ -split "\s+")[-1] } |
    ForEach-Object { ($_ -split "\s+")[2] -replace ".*:" } |
    Select-Object -Unique

# Load TOM
$basePath = "$env:TEMP\tom_nuget\Microsoft.AnalysisServices.retail.amd64\lib\net45"
Add-Type -Path "$basePath\Microsoft.AnalysisServices.Core.dll"
Add-Type -Path "$basePath\Microsoft.AnalysisServices.Tabular.dll"

# Connect to the first port that hosts a model; skip thin-report engines (0 databases)
$server = New-Object Microsoft.AnalysisServices.Tabular.Server
foreach ($p in $ports) {
    $server.Connect("Data Source=localhost:$p")
    if ($server.Databases.Count -eq 0) {
        Write-Output "localhost:$p hosts no model (thin report); trying next port"
        $server.Disconnect()
        continue
    }
    break
}
$model = $server.Databases[0].Model

# Enumerate
foreach ($table in $model.Tables) {
    Write-Output "TABLE: [$($table.Name)] ($($table.Columns.Count) cols, $($table.Measures.Count) measures)"
}
Write-Output "Relationships: $($model.Relationships.Count)"

$server.Disconnect()

端口发现方法:

方法安装类型命令
端口文件非 Store 版 PBI DesktopGet-Content "$env:LOCALAPPDATA\Microsoft\Power BI Desktop\AnalysisServicesWorkspaces\*\Data\msmdsrv.port.txt"
端口文件Store 版 PBI DesktopGet-Content "$env:LOCALAPPDATA\Packages\Microsoft.MicrosoftPowerBIDesktop_*\LocalState\AnalysisServicesWorkspaces\*\Data\msmdsrv.port.txt"
netstat任意netstat -ano | findstr LISTENING | findstr <PID>

2a. 将端口与报表关联(多个实例)

单凭端口无法确定它服务的是哪个报表;连接前请先进行关联,以免修改错误的模型。使用 pbir CLI 并启用 Desktop 的“外部工具访问”预览功能后,pbir desktop list 会显示每个 Desktop PID 及其打开的确切文件。通过进程树将端口映射到对应的 PID(每个 msmdsrv.exe 都是其 PBIDesktop.exe 的子进程):

$conns = Get-NetTCPConnection -State Listen
foreach ($proc in Get-Process msmdsrv -ErrorAction SilentlyContinue) {
    $port = ($conns | Where-Object OwningProcess -eq $proc.Id | Select-Object -First 1).LocalPort
    $parent = (Get-WmiObject Win32_Process -Filter "ProcessId=$($proc.Id)").ParentProcessId
    Write-Output "port $port -> msmdsrv $($proc.Id) -> PBIDesktop $parent"
}

显示 Databases: 0 的引擎对应的是精简报表的工作区;其中不存在本地模型。请改为查询远程模型(pbir model -q 会自动路由到远程模型)。

3. 加载 TOM、连接并保存更改

加载程序集
$basePath = "$env:TEMP\tom_nuget\Microsoft.AnalysisServices.retail.amd64\lib\net45"
Add-Type -Path "$basePath\Microsoft.AnalysisServices.Core.dll"
Add-Type -Path "$basePath\Microsoft.AnalysisServices.Tabular.dll"
Add-Type -Path "$basePath\Microsoft.AnalysisServices.Tabular.Json.dll"
连接
$server = New-Object Microsoft.AnalysisServices.Tabular.Server
$server.Connect("Data Source=localhost:<PORT>")

# PBI Desktop always has exactly one database
$db = $server.Databases[0]
$model = $db.Model
保存更改

只有在完成所有更改后才保存。修改后,使用以下命令持久化更改:

$model.SaveChanges()

更改会立即显示在 PBI Desktop 中。此方法的缺点是,用户无法在 Power BI 中使用 Ctrl+Z 撤销。

断开连接

重要: 修改完成后请记得断开连接。切勿保持连接,否则可能会遗留进程。

$server.Disconnect()
连接属性
Write-Output "Server: $($server.Name)"
Write-Output "Version: $($server.Version)"
Write-Output "Database: $($db.Name)"
Write-Output "Compatibility: $($db.CompatibilityLevel)"

4. 刷新模型

通过 TMSL(表格模型脚本语言)或 TOM 的 RequestRefresh API 触发数据刷新。此操作会重新执行 Power Query/M 表达式,并将数据重新加载到 VertiPaq 引擎中。

# Full refresh of a single table via TMSL
$dbName = $server.Databases[0].Name
$tmsl = '{ "refresh": { "type": "full", "objects": [{ "database": "' + $dbName + '", "table": "Sales" }] } }'
$server.Execute($tmsl)

# Or via TOM RequestRefresh API
$model.Tables["Sales"].RequestRefresh([Microsoft.AnalysisServices.Tabular.RefreshType]::Full)
$model.SaveChanges()
刷新类型行为
full删除数据、重新查询源数据并重新计算 DAX
calculate仅重新计算 DAX(不查询数据源)
automatic引擎按分区决定所需操作
dataOnly重新查询数据源,但跳过 DAX 重新计算

有关详细示例和所有刷新方法,请参阅 refresh-model.md。

5. 使用 DAX 查询

加载 ADOMD.NET
Add-Type -Path "$env:TEMP\tom_nuget\Microsoft.AnalysisServices.AdomdClient.retail.amd64\lib\net45\Microsoft.AnalysisServices.AdomdClient.dll"
打开连接
$conn = New-Object Microsoft.AnalysisServices.AdomdClient.AdomdConnection
$conn.ConnectionString = "Data Source=localhost:<PORT>"
$conn.Open()
执行查询

所有查询最好使用 SUMMARIZECOLUMNS。 如有需要,请在线查看 dax.guide,了解 DAX 函数。

重要: ADOMD.NET 返回的列名带有完整限定信息,但表名不带引号(例如,Brands[Brand Class],而不是 Brand Class;度量值投影返回的名称为 [@Alias])。不要通过短名称访问列($reader["Brand Class"])——这会静默失败并返回空白值。请使用 $reader.GetName($i) 获取列名,再通过索引访问:

$cmd = $conn.CreateCommand()
$cmd.CommandText = "EVALUATE SUMMARIZECOLUMNS('Table'[Column], ""@MeasureName"", [Measure])"

$reader = $cmd.ExecuteReader()

# Always iterate by index and use GetName() to map columns
while ($reader.Read()) {
    for ($i = 0; $i -lt $reader.FieldCount; $i++) {
        Write-Output "$($reader.GetName($i)): $($reader.GetValue($i))"
    }
    Write-Output "---"
}
$reader.Close()
DAX 规则
  • 始终使用完整限定名引用列,表名用单引号括起:'Sales'[Amount],而不是 [Amount]。这适用于所有位置——度量值、计算列和查询。未限定的列会导致歧义错误。
  • DAX 中的表名始终用单引号括起:'Sales'[Amount]、'D&D 5E Monsters'[CR]。为保持一致,即使是 Sales 这样的简单名称,也应写作 'Sales'。
  • 度量值引用是唯一的例外——始终不带限定名:[Total Revenue]
  • DAX 字符串字面量使用双引号,在 PowerShell here-string 中用 "" 转义
查询模式
# Full table scan
$cmd.CommandText = "EVALUATE 'Sales'"

# Filtered with CALCULATETABLE
$cmd.CommandText = "EVALUATE CALCULATETABLE('Sales', 'Sales'[Region] = ""West"")"

# Aggregation
$cmd.CommandText = "EVALUATE SUMMARIZECOLUMNS('Date'[Year], ""@Total"", SUM('Sales'[Amount]))"

# Scalar via ROW
$cmd.CommandText = "EVALUATE ROW(""Result"", COUNTROWS('Sales'))"

# DMV queries (model metadata via SQL-like syntax)
$cmd.CommandText = "SELECT * FROM `$SYSTEM.TMSCHEMA_TABLES"
$cmd.CommandText = "SELECT * FROM `$SYSTEM.TMSCHEMA_MEASURES"
$cmd.CommandText = "SELECT * FROM `$SYSTEM.TMSCHEMA_COLUMNS"
$cmd.CommandText = "SELECT * FROM `$SYSTEM.TMSCHEMA_RELATIONSHIPS"
关闭连接
$conn.Close()

6. 修改语义模型

所有修改都需要 TOM 连接(第 3 节)。每批更改后调用 $model.SaveChanges()。

A. 按对象类型执行 CRUD

如需查看每种对象类型的完整 CRUD 示例,请参阅 tom-object-types.md。

常见对象类型及其 TOM 集合(并非详尽列表——完整命名空间请参阅 Microsoft TOM API 文档):

对象集合创建读取更新删除
表$model.TablesNew-Object ...Table$model.Tables["Name"]设置属性.Remove($obj)
列$table.ColumnsNew-Object ...DataColumn$table.Columns["Name"]设置属性.Remove($obj)
度量值$table.MeasuresNew-Object ...Measure$table.Measures["Name"]设置属性.Remove($obj)
计算列$table.ColumnsNew-Object ...CalculatedColumn按类型筛选设置 .Expression.Remove($obj)
计算表$model.Tables表 + 计算分区检查分区类型设置分区表达式.Remove($obj)
关系$model.RelationshipsNew-Object ...SingleColumnRelationship索引或筛选设置属性.Remove($obj)
层次结构$table.HierarchiesNew-Object ...Hierarchy$table.Hierarchies["Name"]修改级别.Remove($obj)
角色$model.RolesNew-Object ...ModelRole$model.Roles["Name"]设置权限.Remove($obj)
透视$model.PerspectivesNew-Object ...Perspective$model.Perspectives["Name"]切换成员资格.Remove($obj)
区域性$model.CulturesNew-Object ...Culture$model.Cultures["en-US"]设置翻译.Remove($obj)
分区$table.PartitionsNew-Object ...Partition$table.Partitions["Name"]设置源/表达式.Remove($obj)
注释任意对象$obj.Annotations.Add(...)$obj.Annotations["Key"]设置 .Value.Remove($obj)
表达式$model.ExpressionsNew-Object ...NamedExpression$model.Expressions["Name"]设置 .Expression.Remove($obj)
数据源$model.DataSourcesNew-Object ...StructuredDataSource$model.DataSources["Name"]设置连接.Remove($obj)
计算组$model.Tables带有 CalculationGroup 的表按类型筛选添加/移除项.Remove($obj)

快速示例(内联):

# Add a measure
$m = New-Object Microsoft.AnalysisServices.Tabular.Measure
$m.Name = "Total Revenue"
$m.Expression = "SUM(Sales[Amount])"
$m.FormatString = "`$#,0"
$m.Description = "Sum of all sales amounts"
$model.Tables["Sales"].Measures.Add($m)

# Add a relationship
$rel = New-Object Microsoft.AnalysisServices.Tabular.SingleColumnRelationship
$rel.Name = "Sales_to_Date"
$rel.FromColumn = $model.Tables["Sales"].Columns["DateKey"]
$rel.ToColumn = $model.Tables["Date"].Columns["DateKey"]
$rel.FromCardinality = [Microsoft.AnalysisServices.Tabular.RelationshipEndCardinality]::Many
$rel.ToCardinality = [Microsoft.AnalysisServices.Tabular.RelationshipEndCardinality]::One
$model.Relationships.Add($rel)

# Rename a column
$model.Tables["Sales"].Columns["amt"].Name = "Amount"

# Hide a table
$model.Tables["Bridge"].IsHidden = $true

# Delete a measure
$m = $model.Tables["Sales"].Measures["Old Measure"]
$model.Tables["Sales"].Measures.Remove($m)

# Add a role with RLS
$role = New-Object Microsoft.AnalysisServices.Tabular.ModelRole
$role.Name = "Region Filter"
$role.ModelPermission = [Microsoft.AnalysisServices.Tabular.ModelPermission]::Read
$model.Roles.Add($role)
$tp = New-Object Microsoft.AnalysisServices.Tabular.TablePermission
$tp.Table = $model.Tables["Sales"]
$tp.FilterExpression = "[Region] = USERNAME()"
$role.TablePermissions.Add($tp)

$model.SaveChanges()
B. 探索对象类型、属性和设置值

如需查看完整的 TOM 对象类型表、用于探索属性和枚举值的 PowerShell 反射模式,以及读取/设置属性的示例,请参阅 references/tom-object-types.md。

7. 验证 DAX 表达式

保存度量值/列表达式之前,请针对实时模型试执行表达式以进行验证。这样可以发现语法错误、缺失的列引用和循环依赖,而不会持久化错误的元数据。

# Validate a DAX expression before adding it as a measure
$testExpr = "SUM('Sales'[Amount]) / COUNTROWS('Sales')"
$cmd = $conn.CreateCommand()
$cmd.CommandText = "EVALUATE ROW(`"@Test`", $testExpr)"
try {
    $reader = $cmd.ExecuteReader()
    $reader.Close()
    Write-Output "VALID"
} catch {
    Write-Output "INVALID: $($_.Exception.Message)"
}

对于计算表表达式,请用 COUNTROWS 包装:

$tableExpr = "CALENDAR(DATE(2020,1,1), DATE(2030,12,31))"
$cmd.CommandText = "EVALUATE ROW(`"@Count`", COUNTROWS($tableExpr))"

对于筛选表达式(RLS),请使用 CALCULATETABLE 进行测试:

$filterExpr = "'Sales'[Region] = `"West`""
$cmd.CommandText = "EVALUATE CALCULATETABLE(ROW(`"@OK`", 1), $filterExpr)"

8. 事务与回滚

SaveChanges() 会在一个隐式事务中应用所有待处理的修改。如果任何对象验证失败,整个批次都会自动回滚。

对于需要在提交前进行检查或回滚的多步骤工作流:

try {
    # Make changes (not yet persisted)
    $model.Tables["Sales"].Measures["Revenue"].Name = "Total Revenue"
    $model.Tables["Sales"].Measures["Cost"].Name = "Total Cost"

    # Inspect before committing (changes are local to this connection)
    foreach ($m in $model.Tables["Sales"].Measures) {
        Write-Output "  [$($m.Name)]"
    }

    # Commit all changes atomically
    $model.SaveChanges()
    Write-Output "Committed"
} catch {
    # Discard all uncommitted changes
    $model.UndoLocalChanges()
    Write-Output "Rolled back: $($_.Exception.Message)"
}

UndoLocalChanges() 会丢弃自上次 SaveChanges() 以来所做的所有修改。这是 PBI Desktop 的回滚机制;本地 Analysis Services 实例没有显式的事务开始/提交 API。

9. 模型验证

保存前验证

TOM API 不提供公共的 Validate() 方法。验证会在 SaveChanges() 期间隐式进行(失败时会回滚整个批次)。如需在保存前验证,请手动检查对象:

# Check measures have valid expressions (non-empty)
foreach ($m in ($model.Tables | ForEach-Object { $_.Measures }) ) {
    if ([string]::IsNullOrWhiteSpace($m.Expression)) {
        Write-Output "WARNING: Measure [$($m.Name)] in [$($m.Table.Name)] has no expression"
    }
}

# Check relationships reference valid columns
foreach ($rel in $model.Relationships) {
    $sr = [Microsoft.AnalysisServices.Tabular.SingleColumnRelationship]$rel
    if ($sr.FromColumn -eq $null -or $sr.ToColumn -eq $null) {
        Write-Output "WARNING: Relationship [$($sr.Name)] has null column references"
    }
}

# Check for duplicate measure names across tables
$names = @{}
foreach ($m in ($model.Tables | ForEach-Object { $_.Measures })) {
    if ($names.ContainsKey($m.Name)) {
        Write-Output "WARNING: Duplicate measure name [$($m.Name)] in [$($m.Table.Name)] and [$($names[$m.Name])]"
    }
    $names[$m.Name] = $m.Table.Name
}

10. 查找文件路径并编辑元数据文件

查找打开的文件路径

TOM 不会直接公开 .pbix/.pbip 文件路径。

主要方法 — Desktop bridge:pbir desktop list 会报告每个正在运行的实例打开的确切文件(需要 pbir CLI 和“external tool access”预览功能;请参阅第 2a 节)。仅在此方法不可用时,才使用以下方法。

备用方法 — User.zip 中的 FileHistory(适用于 Store 和非 Store 版本):

# Read the most recently opened file from PBI Desktop's settings
$userZip = "$env:USERPROFILE\Microsoft\Power BI Desktop Store App\User.zip"
# For non-Store installs: "$env:LOCALAPPDATA\Microsoft\Power BI Desktop\User.zip"

Add-Type -Assembly System.IO.Compression.FileSystem
$z = [System.IO.Compression.ZipFile]::OpenRead($userZip)
$entry = $z.Entries | Where-Object { $_.Name -eq 'Settings.xml' }
$reader = New-Object System.IO.StreamReader($entry.Open())
$content = $reader.ReadToEnd()
$reader.Close()
$z.Dispose()

# Extract FileHistory entries (ordered by lastAccessedDate, most recent first)
$history = ($content -split '(?=<Entry)' | Where-Object { $_ -match 'FileHistory' })[0]
$json = [regex]::Match($history, 'Value="s\[(.*?)\]"').Groups[1].Value -replace '&quot;', '"'
$files = $json | ConvertFrom-Json
$files | Select-Object filePath, lastAccessedDate | Format-Table -AutoSize

第一项是最近打开的文件。Mac 上的文件(通过 Parallels)会显示为 \\Mac\Home\... 路径。

限制: 此方法并不完全可靠——它读取的是最近打开的文件历史记录,而非当前打开的文件。如果打开了多个 PBI Desktop 实例,或者历史记录中最近访问的文件并非当前打开的文件,结果可能有误。如有任何不确定之处,请向用户确认。

备用方法 — 窗口标题(仅适用于非 Store 版 PBI Desktop):

Get-Process PBIDesktop -ErrorAction SilentlyContinue | Select-Object Id, MainWindowTitle

注意: Store 版 PBI Desktop(来自 Microsoft Store / WindowsApps)不会在窗口标题中显示文件路径——请改用上述 User.zip 方法。

备用方法 — msmdsrv 命令行(提供工作区路径,而非文件路径):

# Useful for finding the port; does NOT reveal the source file path
(Get-WmiObject Win32_Process -Filter "Name='msmdsrv.exe'").CommandLine
编辑 PBIP 元数据文件(连接、报表、模型)

对于 .pbip 项目,元数据文件是磁盘上可读且可直接修改的 JSON/TMDL 文件。

常见目标:

文件用途技能
<Name>.Report/definition.pbir报表到模型的连接(byPath 或 byConnection)pbip
<Name>.Report/definition/report.json报表级设置、主题、筛选器pbir-format
<Name>.SemanticModel/definition/*.tmdl模型元数据(表、度量值、关系)tmdl
<Name>.SemanticModel/definition/expressions.tmdlM/Power Query 共享表达式和参数tmdl

如需了解这些文件的语法、结构和编辑模式,请从 pbip plugin 加载相关技能:

  • pbip —— 项目结构、文件类型、.pbir 连接、分支
  • pbir-format —— report.json、visual.json、主题、筛选器、PBIR JSON 架构
  • tmdl —— TMDL 语法、度量值、列、角色、关系
重新加载外部文件编辑

Power BI Desktop 不会监视外部文件的更改;报表打开时在磁盘上所做的编辑会被静默忽略,或在下次 Desktop 保存时被覆盖。要应用更改,请按以下优先顺序操作:

  1. TOM 修改($model.SaveChanges())会立即应用到正在运行的实例。模型元数据优先使用此方法。
  2. PBIR 报表定义编辑(页面、视觉对象)可通过 pbir desktop refresh "Report.Report" 热重载到打开的画布中(仅适用于 PBIP/PBIR,不适用于 .pbix;需要预览功能)。StaticResources 下的主题 JSON 编辑不会热重载;请改为关闭并重新打开。如果实例有未保存的更改,Desktop 会先保存,可能覆盖磁盘上的编辑。
  3. 其他所有情况(磁盘上的 TMDL 编辑、主题文件、.pbix):关闭 Power BI Desktop,编辑后重新打开。

对于 报表(PBIR)文件,Desktop Bridge 可以将磁盘上的编辑重新加载到打开的画布中,无需重新打开(使用 file.reload/v1 管道方法,或在其不可用时使用 powerbi-desktop npm CLI);请参阅第 13 节。模型(TMDL)的磁盘编辑仍需关闭并重新打开,或使用上文所述的实时 TOM SaveChanges()。

Microsoft 文档
主题URL
TOM API 参考learn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular
TOM 概述learn.microsoft.com/en-us/analysis-services/tom/introduction-to-the-tabular-object-model-tom-in-analysis-services-amo
ADOMD.NET 参考learn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.adomdclient
客户端库learn.microsoft.com/en-us/analysis-services/client-libraries
DMV 参考learn.microsoft.com/en-us/analysis-services/instances/use-dynamic-management-views-dmvs-to-monitor-analysis-services
DAX 参考dax.guide
兼容性级别learn.microsoft.com/en-us/analysis-services/tabular-models/compatibility-level-for-tabular-models-in-analysis-services

要获取最新的 TOM/ADOMD.NET 参考文档,请搜索 https://learn.microsoft.com 并抓取相关页面。根据用户的请求进行搜索,并视需要执行多次搜索,以便在继续之前收集足够的上下文。

11. 使用 EVALUATEANDLOG 调试 DAX

EVALUATEANDLOG(<Value>, [Label], [MaxRows]) 会包装任意 DAX 表达式,原样返回该表达式,并通过跟踪事件以 JSON 格式输出中间结果。仅适用于 PBI Desktop。

以编程方式捕获 TOM Trace API 可免去使用外部工具(DAX Debug Output、SQL Server Profiler、DAX Studio)的需要。订阅 DAXEvaluationLog 跟踪事件(枚举 ID 135),通过 Register-ObjectEvent 使用同步 ArrayList 捕获事件,并解析 $Event.SourceEventArgs.TextData 中的 JSON。

关键实现细节:Register-ObjectEvent -Action 会在单独的 PowerShell 运行空间中运行。操作块中的 $global: 变量不共享作用域。请通过 -MessageData 传递同步集合:

$evalEvents = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
$job = Register-ObjectEvent -InputObject $trace -EventName "OnEvent" -MessageData $evalEvents -Action {
    $Event.MessageData.Add($Event.SourceEventArgs) | Out-Null
}

跟踪事件异步传递:DAXEvaluationLog 事件通常会在查询返回后 2-3.5 秒到达,因此短暂固定休眠会导致漏掉事件。读取结果前,请轮询已捕获事件的数量(最多约 10 秒,每次间隔 500ms)。热缓存运行仍会触发该事件;不要依赖清除缓存来触发事件。仅在需要冷缓存计时时清除 VertiPaq 缓存:

$server.Execute('{ "clearCache": { "object": { "database": "' + $db.Name + '" } } }') | Out-Null

常见调试模式:

模式方法
度量值链分解包装每个中间步骤:EVALUATEANDLOG([Step1], "Label1")
检查筛选上下文跟踪带有和不带有 ALL/REMOVEFILTERS 的 CALCULATE
检测 BLANK 与零在比较之前跟踪该值;在 DAX 中,BLANK = 0 为 TRUE
变量上下文陷阱同时跟踪 VAR 值和 CALCULATE 结果;证明 VAR 不会重新求值
诊断总计按行级别和总计级别跟踪分子与分母
检查表表达式包装 CALCULATETABLE 结果;跟踪显示实际参与聚合的行

完整设置说明、JSON 负载结构、事件批处理行为及所有调试模式,请参阅 evaluateandlog-debugging.md。

12. 性能分析

以编程方式实现 DAX Studio 的 Server Timings 功能。订阅 QueryEnd、VertiPaqSEQueryEnd 和 VertiPaqSEQueryCacheMatch 跟踪事件,测量每个查询的公式引擎(FE)与存储引擎(SE)耗时。

关键公式: FE 耗时 = 总耗时 - SE 扫描耗时总和

重要提示: VertiPaqSEQueryCacheMatch 不支持 Duration 或 CpuTime 列;添加这些列会导致 $trace.Update() 抛出异常。对于缓存匹配事件,只添加 TextData + EventClass。

工作流:

  1. 创建包含性能事件的跟踪(列兼容性请参阅参考文档)
  2. 清除缓存(使用 TMSL clearCache),以获取冷缓存耗时
  3. 通过 ADOMD.NET 执行 DAX
  4. 解析跟踪事件:使用 QueryEnd 获取总耗时,使用 VertiPaqSEQueryEnd 获取每次扫描的 SE 耗时
  5. 比较冷缓存与热缓存的结果,以衡量缓存带来的收益

统计抽样: 单次测量结果会有噪声。更改前后务必各取 6-12 个样本,并比较中位数(而非均值)。如果取值范围有明显重叠,差异很可能只是噪声。丢弃首次冷缓存运行,将其作为预热。Measure-QueryMedian 辅助函数请参阅参考文档。

可视化查询分析: 根据 PBIR visual.json 定义构造 SUMMARIZECOLUMNS 查询。列投影将成为分组列;度量值投影将成为度量值引用;Aggregation.Function 映射为 SUM (0)、MIN (1)、MAX (2)、COUNT (3)、AVERAGE (4)。

有关完整设置、耗时解读、抽样模式和 PBIR 到 DAX 的转换,请参阅 performance-profiling.md。

13. 使用报表画布(桌面桥接)

上面的 TOM 连接负责驱动模型:表、度量值、关系、角色、刷新。它无法操作报表画布(页面和视觉对象)。Power BI Desktop 为此提供了第二个独立的本地 API:桌面桥接,它是一个按进程运行的 JSON-RPC 服务,位于 Windows 命名管道 \\.\pipe\pbi-desktop-bridge-<PID> 上。将两者配合使用,即可更改模型并立即确认报表重新渲染。

安装 pbir CLI 后,它会封装同一管道;优先使用它,而不是直接操作管道:

pbir desktop list                                                             # PID + open file per instance
pbir model "Report.Report" -q 'EVALUATE ROW("Check", [New Measure])'           # engine-level check
pbir desktop refresh "Report.Report"                                          # reload on-disk PBIR into the canvas
pbir desktop screenshot "Report.Report/Page Name.Page" -o verify.png          # inspect rendering

单引号括起的 PowerShell 参数可保留其中嵌入的 DAX 引号,无需使用特定于 Shell 的停止解析标记。

如果没有 pbir,可像此技能操作 TOM/ADOMD 一样,直接从 PowerShell 操作管道。这需要启用 Desktop 桥接的预览设置(文件 > 选项和设置 > 选项 > 预览功能,然后重启)。通过枚举管道目录自动发现 PID;然后使用 JSON-RPC:application.state.get/v1 返回打开的文件路径(currentFilePath,以便桥接定位磁盘上的 PBIP),file.reload/v1 将磁盘上的 PBIR 重新加载到画布,report.snapshot.capture/v1 则返回页面 PNG。

模型与报表的配合流程:使用 TOM 编辑模型并执行 $model.SaveChanges()(实时应用),然后重新加载并截取报表,以确认视觉对象反映了更改(例如度量值重命名、新格式字符串或修复的关系)。磁盘上的报表(PBIR)更改可通过 reload 加载;磁盘上的模型(TMDL)更改以及 StaticResources 下的主题文件仍需重新打开,因此模型更改优先使用实时 TOM。桥接操作的是 Windows 应用,因此在 macOS 上运行时,请在 Parallels 虚拟机中使用它(参见 parallels-macos.md)。

有关完整命令集、PID 选择、JSON-RPC 方法集合(bridge.manifest、application.state.get/v1、file.reload/v1、report.snapshot.capture/v1),以及它如何补充 Analysis Services 本地 API,请参见 desktop-bridge.md。若要更改视觉对象、页面、格式、筛选器或书签,请转到 pbir-cli 技能(reports 插件);此处的 Desktop Bridge 仅负责重新加载和截屏,不会编辑报表。

替代方案(仅当直接操作原始管道遇到问题、帧处理或编码问题,或构建版本更改了参数形状时):使用 pbir desktop 命令(reports 插件的 pbir-cli 技能),它们封装了这些相同的方法。参见 desktop-bridge.md。

参考资料

技能参考:

  • TOM 对象类型 CRUD - 每种对象类型的完整 CRUD 示例,包括 UDF、Direct Lake、KPI 注释
  • 注释和扩展属性 - 标准 PBI 注释、Tabular Editor 表组、自动日期/时间、字段参数、查询组、自定义注释
  • 日历列组 - 通过 TOM 定义公历、财政日历和基于 ISO 周的日历;时间单位、主列/关联列
  • DAX 表达式位置 - DAX 在模型中的位置:度量值、计算列/表、计算项、格式字符串、详细信息行、RLS、UDF
  • DAX 陷阱 - 已弃用/不建议使用的函数、Agent 从 SQL/Python/M 中臆测出的不存在的函数、常见语法错误、BLANK 与 NULL
  • EVALUATEANDLOG 调试 - 通过 TOM Trace API 进行程序化 DAX 调试;捕获中间结果、清除缓存,以及针对常见 DAX 问题的六种调试模式
  • 性能分析 - 通过 Trace API 获取 DAX 服务器计时;FE/SE 时间拆分、冷/热缓存对比、PBIR 视觉对象到 DAX 的转换、跟踪事件列兼容性
  • 查询侦听器 - 通过 DMV 轮询捕获实时视觉对象 DAX 查询;解读查询结构、计时和筛选模式
  • 导出模型 - 通过 Tabular Editor CLI、fab CLI 或 TOM 序列化器导出为 BIM/TMDL
  • 加载 TMDL/BIM 文件 - 将本地 TMDL 文件夹或 BIM 文件离线加载到 TOM;检查、修改、重新序列化,并通过 fab CLI 部署
  • VertiPaq 统计信息 - 列基数、字典/数据大小、按表统计的内存用量,以及通过 DMV 获取的服务器计时
  • 刷新模型 - 所有刷新方式(TMSL、TOM RequestRefresh、ADOMD.NET)
  • macOS + Parallels 指南 - PBI Desktop 在 Parallels 虚拟机中运行时,如何从 macOS 连接
  • DAX 库包 - 从 daxlib.org 安装可复用的 DAX UDF 包;DaxLib.SVG、PowerofBI.IBCS、包结构、注释
  • 桌面桥接(报表画布) - 通过原始命名管道 JSON-RPC API(PowerShell;或 pbir desktop 命令)重新加载打开的报表画布并截取屏幕截图;将模型 (TOM) 编辑与报表验证配对

技能根目录中的 CLI 工具:

  • daxlib -- 用于浏览、下载和安装 daxlib.org 中 DAX 库包的 CLI。脚本位于 daxlib.sh(需要 bash + jq)。模型操作(添加/更新/移除)通过 dotnet run 调用 scripts/daxlib-tom/(需要 .NET 8 SDK)。在 macOS 上,模型操作会自动通过 Parallels 执行。完整命令参考请参阅 daxlib.md。

Agent:

  • query-listener -- 执行任务以实时捕获视觉对象 DAX 查询;轮询 DISCOVER_SESSIONS 并报告查询文本和计时信息

scripts/ 中的示例脚本:

  • connect-and-enumerate.ps1 - 连接到 PBI Desktop 并列出所有表、列、度量值和关系
  • explore-model.ps1 - 分层枚举元数据(表、列、度量值、层次结构、分区、关系、角色、透视、区域设置、表达式、数据源)
  • query-dax.ps1 - 通过 ADOMD.NET 执行 DAX 查询并格式化输出
  • refresh-table.ps1 - 通过 TMSL 刷新表或整个模型,并可配置刷新类型
  • modify-tom-objects.ps1 - 创建表、重命名度量值、设置文件夹和格式、隐藏列、创建关系(支持撤销)
  • create-field-parameter.ps1 - 根据度量值列表创建字段参数表,并包含所有必需的元数据
  • debug-dax.ps1 - 使用 EVALUATEANDLOG 捕获跟踪信息并记录性能耗时,以调试 DAX;自动检测端口、枚举模型度量值,并提供 Invoke-DebugQuery 辅助函数
  • load-tmdl.ps1 - 将本地 TMDL 文件夹或 BIM 文件加载到 TOM 离线处理(无需运行中的引擎),枚举模型,可选择添加度量值并保存回文件
  • connect-from-mac.sh - macOS 包装脚本,通过 prlctl exec 在 Parallels 虚拟机中运行 PowerShell 脚本

外部参考:

评论

支持 Markdown