mcpField
D
dv-data

dv-data

github.com/microsoft数据与数据库248212 次安装今天更新
代码仓库

关于 dv-data

dv-data 是用于在 Dataverse 中创建、更新、删除和导入记录的技能。它根据操作规模与需求,指导构建者选择 MCP、Dataverse CLI 或官方 Python SDK。

主要功能

  1. 01创建、更新和删除记录
  2. 02使用备用键进行 Upsert
  3. 03批量记录操作
  4. 04CSV 导入与查找解析
  5. 05文件列上传

适用场景

  1. 01交互式写入少量记录
  2. 02导入带有关联记录查找的 CSV 数据
  3. 03执行可重复的批量 Upsert
  4. 04将文件上传至 Dataverse 列

MCP 相关

对于少量交互式写入,该技能建议使用 Dataverse MCP 工具,例如 create_record、update_record 和 delete_record;每次调用最多处理 25 条记录。

安装

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

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

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

包含的文件

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

references/
erp-writes.md
multi-table-fk-import.md
sample-data-generation.md

详细介绍

技能:数据 — 创建、更新、删除和批量导入

此技能使用 Python 和 Dataverse CLI。 不要使用 Node.js、JavaScript 或任何其他语言编写 Dataverse 脚本。如果你准备运行 npm install 或编写 .js 文件,请立即停止——你偏离了正确方向。请参阅概览技能中的硬性规则。

ERP 例外(包括仅规划的请求): 加载 dv-overview,然后阅读 references/erp-writes.md。ERP 写入和 DMF 导入不使用下文的 Dataverse SDK 指南。即使请求不执行操作,仍可加载技能并阅读本地参考资料。

所有数据写入操作均使用 Microsoft 官方 Power Platform Dataverse Client Python SDK。

官方 SDK: https://github.com/microsoft/PowerPlatform-DataverseClient-Python PyPI 包: PowerPlatform-Dataverse-Client(这是唯一的官方包——不要使用 dataverse-api 或其他非官方包) 状态: GA(1.1.0,Production/Stable)

技能适用范围

需求改用
查询或读取记录dv-query
创建表、列、关系、表单、视图dv-metadata
导出或部署解决方案dv-solution
ERP 写入请参阅 references/erp-writes.md

为写入操作选择 MCP、CLI 或 SDK

CLI 快捷方式: 如果 dataverse auth who 显示有活动配置文件,CLI 命令(data create/update/delete/upsert/associate/upload)即可立即使用——无需 .env、auth.py 或 pip。SDK 和批量操作仍需要设置工作区。

如果有 MCP 工具可用(create_record、update_record、delete_record),对于少量交互式写入,这是最快捷的方式——每次调用最多处理 25 条记录,无需编写脚本。Dataverse CLI(dataverse data create/update/upsert/delete)可通过无头单行命令处理单条记录写入、关联/取消关联和文件上传。SDK 是处理超过 25 条记录的批量写入、数据转换、重试逻辑、CSV 导入或仅 SDK 支持的操作(使用备用键进行 upsert——MCP 没有 upsert 工具)的默认选择。根据工作量和数据特征选择合适的方式。

使用脚本执行写入时,使用 SDK,而不是手动编写 HTTP 请求

MCP/CLI/SDK 的选择取决于功能(如上文所述;另请参阅概览中的工具功能 / 硬性规则 2)。本节范围更窄:一旦决定通过脚本执行写入,对于 SDK“支持”列表中的操作,应使用 SDK,而不是手动编写 urllib/requests——SDK 已处理身份验证、分页和重试,无需重新实现这些功能。对于 SDK 未涵盖的少数操作,请使用 dataverse api 备用方式——不要手动编写 urllib。

正确导入(完整脚本中始终先执行 sys.path.insert——参阅下文的设置):

from auth import get_client

SDK 支持的操作中,以下做法是错误的:

from auth import get_token, load_env  # WRONG for SDK-supported ops
import requests                        # WRONG for SDK-supported ops

get_token() 和 requests 仅用于确实没有托管方式的功能(全局选项集、未绑定操作)——即使如此,也优先使用托管的 dataverse api 备用方式。表单/视图、聚合和 N:N 读取均由 SDK 支持;请参阅 dv-query 和 dv-metadata。


此 SDK 支持的内容(数据操作)

  • 记录写入:创建、更新、删除
  • 写入工作流中的记录读取(例如,查找项解析)——独立查询请参阅 dv-query
  • Upsert(支持备用键)
  • 批量操作:CreateMultiple、UpdateMultiple、UpsertMultiple、批量删除(records.delete(table, [ids]) 会运行服务端 BulkDelete 操作)、多操作批处理(client.batch)
  • 文件列上传(大于 128MB 的文件采用分块上传)
  • 支持 HTTP 连接池的上下文管理器

此 SDK 不支持的功能

表单/视图(systemform/savedquery)是普通记录——使用 client.records.* 创建/修改(参见 dv-metadata),并使用 records.list(expand=...) 读取 N:N。对于以下真正不支持的功能,优先使用托管的 dataverse api 作为替代方案,而不是直接使用 urllib:

  • 全局选项集——参见 dv-metadata
  • N:N 记录关联——CLI dataverse data associate,或 POST /api/data/v9.2/<entity>(<id>)/<nav-property>/$ref
  • $apply 聚合——使用 client.query.fetchxml();参见 dv-query
  • 无绑定操作(例如 PublishXml、InstallSampleData)——dataverse api request/invoke
Dataverse CLI 数据示例(可直接复制粘贴)

所有 dataverse 命令都接受 --context,用于技能归属标记(全局标志)。

# Create a record (--table is the EntitySet name)
dataverse data create --table accounts --data '{"name":"Contoso"}' --return --json --context "app=dataverse-skills/<ver>;skill=dv-data;agent=<agent>"

# Update by GUID
dataverse data update --table accounts --id <guid> --data '{"name":"Contoso (updated)"}' --json --context "app=dataverse-skills/<ver>;skill=dv-data;agent=<agent>"

# Upsert by alternate key (idempotent — safe to re-run)
dataverse data upsert --table accounts --key "accountnumber='ACC-001'" --data '{"name":"Contoso Ltd"}' --json --context "app=dataverse-skills/<ver>;skill=dv-data;agent=<agent>"

# Delete (--no-confirm skips the prompt)
dataverse data delete --table accounts --id <guid> --no-confirm --context "app=dataverse-skills/<ver>;skill=dv-data;agent=<agent>"

# Associate two records (N:N or lookup)
dataverse data associate --table accounts --id <guid> --relationship contact_customer_accounts --related contacts --related-id <contact-guid> --context "app=dataverse-skills/<ver>;skill=dv-data;agent=<agent>"

# Disassociate (N:N — pass --related-id; clear a lookup — omit --related-id)
dataverse data disassociate --table accounts --id <guid> --relationship contact_customer_accounts --related-id <contact-guid> --context "app=dataverse-skills/<ver>;skill=dv-data;agent=<agent>"

# Upload a file to a file column (--table takes LogicalName, not EntitySet)
dataverse data upload --table account --id <guid> --column new_document --file report.pdf --context "app=dataverse-skills/<ver>;skill=dv-data;agent=<agent>"

# Describe entity schema (attributes, relationships, actions)
dataverse data describe --table account --include all --json --context "app=dataverse-skills/<ver>;skill=dv-data;agent=<agent>"

# Invoke a discovered custom API by name (use 'api list' to find names)
dataverse api invoke <CustomApiName> --target dataverse --param Input=value --context "app=dataverse-skills/<ver>;skill=dv-data;agent=<agent>"

# Raw API escape hatch for built-in actions (--target is required)
dataverse api request --target dataverse --path "/api/data/v9.2/WhoAmI" --context "app=dataverse-skills/<ver>;skill=dv-data;agent=<agent>"

设置

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-data")

get_client(skill) 负责处理身份验证、环境 URL 和插件归属标记(User-Agent 标记)。参见 scripts/auth.py。

对于会运行至完成的脚本:使用 with DataverseClient(...) as client: 进行封装,以自动清理连接(推荐)。对于笔记本和交互式会话,直接使用上面的显式客户端更简单。


字段名称大小写规则

大小写错误会导致 400 错误。

属性类型约定示例用途
结构性(列)LogicalName — 始终小写new_name、new_priority记录载荷键
导航(查找列)Navigation Property Name — 区分大小写,与 $metadata 一致new_AccountId@odata.bind 键

SDK 会自动将结构性键转换为小写,但会保留 @odata.bind 键的大小写。


创建记录

guid = client.records.create("new_ticket", {
    "new_name": "Ticket 001",
    "new_priority": 100000002,          # choice column — integer value, not string
    "[email protected]": "/accounts(<account-guid>)",
})
print(f"Created: {guid}")

@odata.bind 注意事项:

  • 键是 Navigation Property Name:[email protected](SDK 会自动保留大小写,但匹配架构名称仍是正确写法)
  • 值为 "/<EntitySetName>(<guid>)"——例如 "/accounts(<guid>)"
  • 如果刚创建了查找列,请等待 5–10 秒再插入。元数据传播延迟会导致“Invalid property”错误。
  • 选项列使用整数值,而非字符串:"new_priority": 100000002(而不是 "High")
常见的 @odata.bind 模式
查找列正确键错误写法
自定义:new_AccountId[email protected][email protected]
系统多态:customerid[email protected][email protected]
系统:parentcustomerid[email protected][email protected]
查找 Navigation Property Name

通过 SDK 创建查找列后,result.lookup_schema_name 就是导航属性名称。

对于现有系统表,请查询:

GET /api/data/v9.2/EntityDefinitions(LogicalName='<entity>')/ManyToOneRelationships
  ?$select=ReferencingEntityNavigationPropertyName,ReferencedEntity

更新记录

client.records.update("new_ticket", "<record-guid>",
    {"new_status": 100000001})

删除记录

# Single record
client.records.delete("new_ticket", "<record-guid>")

# Bulk delete — pass a list of ids; runs the server-side BulkDelete action (one async job, not N calls)
job_id = client.records.delete("new_ticket", [id1, id2, id3])
# use_bulk_delete=False deletes each id sequentially instead

批量创建(SDK 在内部使用 CreateMultiple)

records = [{"new_name": f"Ticket {i}", "new_priority": 100000000} for i in range(500)]
guids = client.records.create("new_ticket", records)
print(f"Created {len(guids)} records")

用量建议:一次性创建记录请使用 CLI dataverse data create。MCP create_record 每次调用最多批量处理 25 条。较大批量请使用 SDK CreateMultiple。

重要: SDK 会通过单个 POST 将所有记录发送到 CreateMultiple。它不会自动分块。Dataverse 没有固定的记录数量限制——约束来自载荷大小和请求超时(SDK 的 POST 默认超时时间:120 秒)。对于较大的数据集,必须在脚本中分块。下面的 bulk_upsert 和 bulk_create 辅助函数采用自适应分块:从 1,000 条开始,成功后加倍(最多 4,000 条),遇到载荷/超时错误时减半,并将大小限制在最后一次成功的值。列数较少的表可以处理比列数较多的表更大的分块。


批量更新

# Broadcast same change to multiple records
client.records.update("new_ticket",
    [id1, id2, id3],
    {"new_status": 100000001})

DataFrame 写回

要从 pandas DataFrame 创建或更新记录,请使用 client.dataframe 命名空间(create/update)。这部分内容记录在 dv-query 中,但它属于写入操作——请将其纳入数据写入工作流:

# Update records — DataFrame must include the primary key column
client.dataframe.update("opportunity", df_updates, id_column="opportunityid")

# Create records — returns a Series of new GUIDs
guids = client.dataframe.create("opportunity", df_new_records)

完整的 client.dataframe 写入参考请参见 dv-query;读取数据请使用 client.query.builder(...).execute().to_dataframe()。


Upsert(备用键)

幂等 — 重新运行相同的导入不会创建重复记录。必须先在表上定义备用键 — 参见 dv-metadata。

请勿在记录正文中包含备用键列。 备用键用于标识记录;记录正文包含要设置的数据。如果同一列同时出现在两者中,UpsertMultiple 会失败并报错“An unexpected error occurred”(单条 upsert 可以容忍,批量操作不行)。

from PowerPlatform.Dataverse.models.upsert import UpsertItem

client.records.upsert("account", [
    UpsertItem(
        alternate_key={"accountnumber": "ACC-001"},
        record={"name": "Contoso Ltd", "description": "Primary account"},
    ),
    UpsertItem(
        alternate_key={"accountnumber": "ACC-002"},
        record={"name": "Fabrikam Inc"},
    ),
])

从 CSV 批量导入

对于可能会重新运行的导入(大多数实际场景),请使用带备用键的 UpsertItem,而不要使用 create() — 参见 references/multi-table-fk-import.md。这里的 create() 模式仅适用于一次性加载。

规模工具原因
1 条记录CLI dataverse data create 或 MCP create_record无需脚本
2–25 条记录MCP create_record每次调用最多批处理 25 条
25 条以上记录SDK client.records.create(table, list)使用 CreateMultiple;对大型数据集分块处理(从 1K 开始,再调整)
import csv, 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-data")

with open("data/customers.csv", newline="", encoding="utf-8") as f:
    rows = list(csv.DictReader(f))

records = [{"new_name": row["name"], "new_email": row["email"]} for row in rows]

# SDK sends all in one POST — chunk to avoid payload/timeout limits
# Start at 1000; for narrow tables (few columns) you can go higher
chunk_size = 1000
for i in range(0, len(records), chunk_size):
    guids = client.records.create("new_customer", records[i:i + chunk_size])
    print(f"Imported {i + len(guids)}/{len(records)} customers", flush=True)
导入期间解析查找项

如果 CSV 中包含人类可读的键(例如 customer_email),但 Dataverse 需要 GUID,请使用查找字典预先解析:

# Build email -> GUID map first
email_to_guid = {}
for r in client.records.list("new_customer", select=["new_customerid", "new_email"]):
    email_to_guid[r["new_email"]] = r["new_customerid"]

# Use it during import
records = []
for row in rows:
    customer_guid = email_to_guid.get(row["customer_email"])
    if not customer_guid:
        print(f"Skipping row — unknown email: {row['customer_email']}")
        continue
    records.append({
        "new_channel": row["channel"],
        "[email protected]": f"/new_customers({customer_guid})",  # verify entity set name via EntityDefinitions
    })

guids = client.records.create("new_interaction", records)
发现系统表的必填字段

在系统表(account、contact、opportunity)中批量创建记录之前:

  1. 使用计划中的最小有效负载创建一条测试记录
  2. 如果引发 HttpError 400,错误消息会指出缺少的必填字段
  3. 某些必填字段由插件强制要求,在 describe 中不可见
  4. 删除测试记录,然后继续批量创建

带外键依赖的多表导入

在多个存在外键关系的表之间导入数据时,必须按依赖顺序运行导入,并使用 UpsertItem + 备用键(幂等,可安全重新运行)。

快速参考:

  1. 创建包含源 ID 列、备用键和查找关系的表(参见 dv-metadata)。
  2. 使用 ThreadPoolExecutor 并行导入 Level 0(无外键依赖)表。每个表内的分块按顺序处理(并发写入会造成死锁)。
  3. 通过查询回填源 ID → GUID 映射(upsert 不会返回 GUID)。
  4. 对每个依赖级别重复上述操作 — Level 1 需要 Level 0 的映射来设置 @odata.bind。

完整模式 — 自适应 bulk_upsert 助手、复合键处理、导入后验证以及首次使用的 bulk_create 变体 — 请参见 references/multi-table-fk-import.md。

关键约束(即使不阅读参考文档也适用):

  • 在同一层级的表之间并行处理,层级之间按顺序处理,每个表内的分块按顺序处理。
  • 备用键列不得同时出现在记录正文中 — UpsertMultiple 会失败。
  • 在执行器中捕获每个表的失败 — 一个表失败不得导致其他表停止。
  • 从 chunk_size=1000 开始;助手会自适应地逐步调整。

错误处理

from PowerPlatform.Dataverse.core.errors import HttpError

try:
    guid = client.records.create("new_ticket", {"new_name": "Test"})
except HttpError as e:
    print(f"Status {e.status_code}: {e.message}")
    if e.details:
        print(f"Details: {e.details}")
    # 400 — bad field name, @odata.bind format, or missing required field
    # 403 — check security roles
    # 404 — table or record not found
    # 429 — rate limited; SDK retries automatically, reduce batch size if persistent

写入 ERP 数据

在关联 ERP 的环境中,对 ERP 实体的写入不会通过 Python SDK 进行。请参见 references/erp-writes.md。


Windows 脚本说明

  • .py 文件中仅使用 ASCII 字符 — 弯引号和长破折号会在 Windows 上导致 SyntaxError。
  • 不要使用 python -c 编写多行代码 — 请改为编写 .py 文件。
  • 在脚本中生成 GUID:使用 str(uuid.uuid4()),不要使用 shell 反引号替换。

示例数据生成

内联生成逼真的示例记录——由架构驱动、不依赖特定表,并采用保护 PII 的默认值(@example.com 邮箱、555-01xx 电话)。

**快速参考:**确认环境 + 数量 + 表 → 查询 EntityDefinitions(LogicalName='<table>')/Attributes?$filter=AttributeOf eq null 获取必填列 → 按 AttributeType 分派(String / Memo / Integer / DateTime / Picklist / 等)→ client.records.create()(数量 >= 10 时使用 CreateMultiple)。

有关架构驱动的 fake() 模板、EntityDefinitions 查询和安全规则,请参阅 references/sample-data-generation.md。

关键不变量:

  • 除非用户明确提供值,否则跳过 Lookup、Uniqueidentifier、State、Status、Owner、Customer 字段。
  • UserLocalizedLabel 可能为 null——请安全解引用。
确认流程示例

生成 N 条示例记录(具有破坏性——预览代码片段,并询问环境):

  • ❌“要针对哪个环境?请提供 Dataverse URL。”
  • ✅“我将使用 TABLE=\"contact\"、COUNT=20 运行示例数据生成代码片段。针对当前活动的 pac auth list 环境,使用 CreateMultiple、.example.com 邮箱和 555-01xx 电话。确认后继续,或指定其他环境。”

为自定义实体生成示例数据(架构未知——用文字说明即可):

  • ❌“我需要了解更多实体信息。有哪些必填字段?”
  • ✅“自定义实体——我将查询 EntityDefinitions,发现 cr123_project 的必填列,然后内联生成 5 条记录,根据 AttributeType 将每列映射到一个生成器,并调用 client.records.create(\"cr123_project\", records)。确认后继续,或告知我其他数量。”

评论

支持 Markdown