mcpField
D
dv-solution

dv-solution

github.com/microsoft云与 DevOps248222 次安装今天更新
代码仓库

关于 dv-solution

dv-solution 是面向 Dataverse 开发者的解决方案管理技能,使用 PAC CLI 与 Python SDK 创建、导出、打包、导入并验证解决方案。它涵盖从源文件往返到环境部署后的组件检查。

主要功能

  1. 01创建解决方案及添加组件
  2. 02导出与解包解决方案
  3. 03打包并导入解决方案
  4. 04导入后验证组件和状态

适用场景

  1. 01将环境中的解决方案拉取为可编辑文件
  2. 02打包源文件并导入目标环境
  3. 03确认导入后表、表单和视图可用

安装

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

$npx skills add https://github.com/microsoft/dataverse-skills --skill dv-solution

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

详细介绍

技能:解决方案

通过 PAC CLI 创建、导出、解包、打包、导入和验证 Dataverse 解决方案。包含使用 Python SDK 进行的导入后验证。

无头/受限出站流量的主机:在线步骤请使用原始 Web API(ExportSolution / ImportSolution)。pac solution pack/unpack 是本地文件操作(无需身份验证),但需要可运行 PAC 的主机——请在具备相应能力的计算机或 CI 运行器上执行。使用 python scripts/auth.py --check 验证出站流量。请参阅 dv-connect/references/headless-hosts.md。

技能边界

需求改用
创建表、列、关系、表单、视图dv-metadata
创建、更新或删除数据记录dv-data
查询或读取记录dv-query
连接到 Dataverse/设置 MCPdv-connect

创建新解决方案

使用 Python SDK 创建发布者和解决方案记录,不要使用原始 HTTP。 发布者和解决方案是标准的 Dataverse 表。client.records.create() 和 client.records.list() 会自动处理身份验证、分页和错误处理,避免原始 urllib 调用引入的 URL 编码、请求头样板代码和 GUID 解析错误。

步骤 1:查找或创建发布者

每个解决方案都属于一个发布者。发布者的 customizationprefix(例如 contoso、sa、lit)会加在每个自定义表、列和关系的架构名称前。这个前缀实际上是永久性的——即使之后更改发布者,现有组件也会永久保留其前缀。

切勿使用默认的 new 前缀。 它无法体现组织身份,会带来命名冲突风险,也表明开发者没有遵循最佳实践。

发现流程——创建发布者之前务必先运行:

import os, sys
sys.path.insert(0, os.path.join(os.getcwd(), "scripts"))
from auth import get_client

# get_client sets a plugin attribution context on the User-Agent header.
# Do not modify the context value — it is a closed schema for server-side
# telemetry (app/skill/agent). Never include secrets or PII.
client = get_client("dv-solution")

# 1. Query for existing non-Microsoft publishers
publishers = client.records.list(
    "publisher",
    filter="customizationprefix ne 'none' and uniquename ne 'MicrosoftCorporation' and uniquename ne 'Microsoftdynamic'",
    select=["publisherid", "uniquename", "friendlyname", "customizationprefix"],
    top=10,
)

if publishers:
    # Show existing publishers and ask user which to use
    print("Existing publishers in this environment:")
    for p in publishers:
        print(f"  {p['uniquename']} (prefix: {p['customizationprefix']}_)")
    # ASK THE USER: "Which publisher should this solution use?"
    # Or: "Should I reuse '<name>' (prefix: <prefix>_)?"
    publisher_id = publishers[0]["publisherid"]  # after user confirms
else:
    # No custom publisher exists — ASK THE USER for prefix
    # "What publisher prefix should I use? (e.g., 'contoso', 'sa', 'lit' — 2-8 lowercase chars)"
    publisher_id = client.records.create("publisher", {
        "uniquename": "<publisheruniquename>",
        "friendlyname": "<Publisher Display Name>",
        "customizationprefix": "<prefix>",   # from user input, NOT 'new'
        "description": "<description>",
    })

规则:

  • 创建新发布者或选择前缀之前,务必先询问用户。切勿将前缀硬编码。
  • 前缀必须与解决方案中已创建的任何表一致——不能混用前缀。
  • 一个发布者可以拥有多个解决方案。尽可能重复使用现有发布者。
步骤 2:创建解决方案记录

使用 SDK 创建解决方案记录(优先于原始 Web API):

import os, sys
sys.path.insert(0, os.path.join(os.getcwd(), "scripts"))
from auth import get_client

# get_client sets a plugin attribution context on the User-Agent header.
# Do not modify the context value — it is a closed schema for server-side
# telemetry (app/skill/agent). Never include secrets or PII.
client = get_client("dv-solution")

# Create the solution record
solution_id = client.records.create("solution", {
    "uniquename": "<UniqueName>",
    "friendlyname": "<Display Name>",
    "version": "1.0.0.0",
    "[email protected]": "/publishers(<publisher_guid>)",
})
print(f"Created solution: {solution_id}")

必填字段:

Table:  solution
Fields: uniquename    = "<UniqueName>"
        friendlyname  = "<Display Name>"
        version       = "1.0.0.0"
        publisherid   = <publisher GUID from step 1>

**注意:**没有 pac solution create 命令。PAC CLI 用于导出/导入/打包/解包,而不是创建解决方案记录。请使用 SDK 或 Web API 创建记录。

步骤 3:添加组件

使用 pac solution add-solution-component 添加表、表单、视图和其他组件:

pac solution add-solution-component \
  --solutionUniqueName <UniqueName> \
  --component <ComponentSchemaName> \
  --componentType <TypeCode> \
  --environment <url>

**注意:**PAC CLI 在此处使用 camelCase 参数(--solutionUniqueName、--componentType),而不是 kebab-case。

常见组件类型代码:

类型代码组件
1Entity(表)
2Attribute(列)
26视图
60表单
61Web 资源
300Canvas 应用
371连接器

对要添加的每个组件重复运行此命令。

替代方法:通过 MSCRM.SolutionName 请求头自动添加

通过 Web API 创建元数据时,请包含 MSCRM.SolutionName 请求头,以便自动将组件添加到解决方案:

headers = {
    "Authorization": f"Bearer {token}",
    "Content-Type": "application/json",
    "MSCRM.SolutionName": "<UniqueName>"
}

**重要:**使用此方法后,请通过 SDK 查询 solutioncomponent 表,确认组件已添加(当前 PAC 中没有 pac solution list-components 命令):

sol = client.records.list("solution",
    filter="uniquename eq '<UniqueName>'", select=["solutionid"], top=1).first()
if sol is not None:
    components = client.records.list("solutioncomponent",
        filter=f"_solutionid_value eq {sol['solutionid']}",
        select=["componenttype", "objectid"])
    print(f"{len(components)} components in the solution")

如果请求头拼写错误或解决方案不存在,组件将被静默创建到默认解决方案中。务必进行验证。

查找解决方案名称

导出前,请确认准确的唯一名称:

pac solution list --environment <url>

UniqueName 列是传递给其他命令的名称。显示名称包含空格;唯一名称不包含空格。

拉取:导出 + 解包

导出或导入前,请确认目标环境。 运行 pac auth list + pac org who,向用户展示输出,并确认与预期环境一致。开发者会在多个环境中工作——不要想当然。

将解决方案导出为非托管格式(事实来源):

pac solution export \
  --name <UniqueName> \
  --path ./solutions/<UniqueName>.zip \
  --managed false \
  --environment <url>

解包为可编辑的源文件:

pac solution unpack \
  --zipfile ./solutions/<UniqueName>.zip \
  --folder ./solutions/<UniqueName> \
  --packagetype Unmanaged

Windows 文件锁竞争。 请像上面那样将导出和解包作为单独命令运行;紧接着串联运行可能会遇到导出后 ZIP 文件短暂被锁定的问题。如果 unpack 因锁定/“正在使用”错误而失败,请稍等片刻后重试,并在删除 zip 前确认解包后的文件夹包含预期组件。

删除 zip——解包后的文件夹才是源:

rm ./solutions/<UniqueName>.zip

提交:

git add ./solutions/<UniqueName>
git commit -m "chore: pull <UniqueName> baseline"
git push

推送:打包 + 导入

将源文件重新打包为 zip:

pac solution pack \
  --zipfile ./solutions/<UniqueName>.zip \
  --folder ./solutions/<UniqueName> \
  --packagetype Unmanaged

导入(大型解决方案建议使用异步方式):

pac solution import \
  --path ./solutions/<UniqueName>.zip \
  --environment <url> \
  --async \
  --activate-plugins

轮询导入状态

异步导入后,检查作业:

pac solution list --environment <url>

导入后验证

导入解决方案后,请确认组件已上线。使用 Python SDK 直接检查——无需外部脚本。

检查表是否存在
info = client.tables.get("<logical_name>")
if info:
    print(f"[PASS] Table '{info.logical_name}' exists")
else:
    print(f"[FAIL] Table '<logical_name>' not found")
检查表单是否已发布
forms = client.records.list(
    "systemform",
    filter="objecttypecode eq '<entity>' and type eq <form_type_code>",
    select=["name", "formid"],
    top=5,
)
# Form type codes: 2 = main, 7 = quick create
检查视图是否存在
views = client.records.list(
    "savedquery",
    filter="returnedtypecode eq '<entity>'",
    select=["name", "savedqueryid", "statuscode"],
    top=10,
)
检查用户的角色分配(N:N $expand)

records.list 会直接传递 $expand,因此可使用 SDK 直接读取 N:N 导航属性:

users = list(client.records.list(
    "systemuser",
    filter="internalemailaddress eq '<email>'",   # fallback: domainname eq '<upn>'
    select=["fullname"],
    expand=["systemuserroles_association($select=name)"],
    top=1,
))
roles = [r["name"] for r in users[0].get("systemuserroles_association", [])] if users else []

或者使用托管的 Dataverse CLI 备用方式(dataverse api request——而非 urllib),或使用带有 link-entity 的 FetchXML:

dataverse api request --target dataverse --method GET \
  --path "/api/data/v9.2/systemusers?%24filter=internalemailaddress eq '<email>'&%24select=fullname&%24expand=systemuserroles_association(%24select=name)&%24top=1" \
  --environment <DATAVERSE_URL> \
  --context "app=dataverse-skills/<ver>;skill=dv-solution;agent=<agent>"

响应中的 value[0].systemuserroles_association 是已分配角色的列表(每个角色都有 name)。

检查导入错误
jobs = client.records.list(
    "importjob",
    select=["importjobid", "solutionname", "startedon", "completedon", "progress"],
    orderby=["startedon desc"],
    top=5,
)

如需查看详细错误历史记录,也可查询 msdyn_solutionhistory:

history = client.records.list(
    "msdyn_solutionhistory",
    filter="msdyn_status eq 1",  # 1 = failed
    select=["msdyn_name", "msdyn_starttime", "msdyn_exceptionmessage"],
    orderby=["msdyn_starttime desc"],
    top=5,
)
验证错误参考
错误原因修复方法
导入后找不到表组件不在解决方案中使用 pac solution add-solution-component 添加
表单检查立即失败发布是异步的等待 30 秒后重试
未分配角色用户尚未配置使用 pac admin assign-user 或 Power Platform 管理中心分配角色
导入作业进度为 0%导入仍在进行60 秒后再次轮询

备注

  • 开发解决方案始终使用 --managed false / --packagetype Unmanaged。托管包用于部署到下游环境(测试、生产)。
  • --activate-plugins 可确保解决方案中已注册的插件在导入时激活。
  • 如果看到“solution already exists”错误,请使用 --import-mode ForceUpgrade 覆盖。
  • 大型解决方案(Sales、Customer Service)可能需要 10–20 分钟才能导入。请耐心等待并轮询,不要重新导入。
  • 上述所有验证查询都需要身份验证。使用 scripts/auth.py 获取凭据/令牌。有关 SDK 查询模式,请参阅 dv-query;有关写入模式,请参阅 dv-data。

评论

支持 Markdown