MS Dynamics Web API 怪癖:多态字段的奇异案例
Source: Dev.to
请提供您希望翻译的正文内容,我将按照要求将其翻译为简体中文,并保留原有的格式、Markdown 语法以及技术术语。谢谢!
如果您曾经与 Microsoft Dynamics 365 / Dataverse 集成,您可能几乎立刻注意到一些奇怪的现象。
当您读取记录时,会看到类似下面的字段:
{
"_ownerid_value": "151F639c-1c73-eb11-b1ab-000d3a253b40"
}但在创建或更新记录时,API 突然要求使用类似下面的格式:
{
"ownerid@odata.bind": "/systemusers(151F639c-1c73-eb11-b1ab-000d3a253b40)"
}如果该字段是多态的,有时属性名称会再次变化:
{
"parentcustomerid_account@odata.bind": "/accounts(ce9eaaef-f718-ed11-b83e-00224837179f)"
}欢迎来到 Dynamics Web API 中最令人困惑的部分之一:查找和多态字段。让我们来拆解一下其中的原理。
Source: …
什么是 Dynamics 中的多态字段?
Dynamics 中的某些关系可以指向多种实体类型。
示例元数据:
{
"LogicalName": "ownerid",
"Targets": [
"systemuser",
"team"
]
}这意味着记录的所有者可以是:
- 系统用户
- 团队
其他多态查找的示例包括:
| 字段 | 可能的目标 |
|---|---|
ownerid | systemuser, team |
customerid | account, contact |
regardingobjectid | 多个实体 |
从概念上讲,这非常强大:模式允许一个关系字段指向多个表。但它也会导致一些有趣的 API 行为。
同一字段的 3 种不同名称
1️⃣ 元数据名称
在模式或元数据中:ownerid
2️⃣ 读取记录
检索记录时,字段会变为 __value。
示例响应:
{
"_ownerid_value": "151F639c-1c73-eb11-b1ab-000d3a253b40"
}额外的注释通常会出现:
{
"_ownerid_value": "GUID",
"_ownerid_value@Microsoft.Dynamics.CRM.lookuplogicalname": "systemuser",
"_ownerid_value@OData.Community.Display.V1.FormattedValue": "John Smith"
}含义:
| 属性 | 含义 |
|---|---|
_ownerid_value | GUID |
lookuplogicalname(注释) | 实体类型 |
FormattedValue(注释) | 显示值 |
因此在读取数据时,需要解释这三条信息才能了解关系。
3️⃣ 写入记录
创建或更新记录时,Dynamics 期望 OData 绑定:@odata.bind。
示例:
{
"ownerid@odata.bind": "/systemusers(151F639c-1c73-eb11-b1ab-000d3a253b40)"
}模式:
@odata.bind : "/<entityset>(GUID)"(其中实体集合名称使用复数形式。)
当多态字段变得更加奇怪时
某些多态查找需要在属性中嵌入实体名称。
示例
{
"parentcustomerid_account@odata.bind": "/accounts(GUID)"
}{
"parentcustomerid_contact@odata.bind": "/contacts(GUID)"
}模式:
<field>_<entity>_@odata.bind属性名称本身会根据目标实体而变化,这可能特别令人困惑。
特殊情况:ownerid
ownerid 的行为略有不同。与其他多态字段不同,不需要在属性后添加实体后缀。以下两种写法都可以:
{
"ownerid@odata.bind": "/systemusers(GUID)"
}{
"ownerid@odata.bind": "/teams(GUID)"
}实体类型是从 URL 中的实体集合推断的,而不是从属性名称推断的。正是这个例外导致开发者常觉得 Dynamics 集成难以预测。
开发者对此的看法
如果你搜索 StackOverflow 或 Dynamics 论坛,你会反复看到相同的问题:
- 为什么 API 返回
_ownerid_value而不是ownerid? - 为什么某些查找需要
@odata.bind,而其他则需要field_entity@odata.bind? - 为什么字段名称会根据是读取还是写入而变化?
在讨论中常见的模式是,开发者通过 反复试验 而非明确的文档来发现正确的格式。API 体现了多年演变的平台架构,这种复杂性渗透到了集成层。
为什么这对集成很重要
如果您正在构建与 Dynamics 的集成——尤其是同步引擎、SaaS 连接器或数据管道——这种复杂性会迅速累积。您的集成代码最终需要处理:
- 多态查找检测
- 实体类型解析
- OData 绑定格式
- 多种命名约定
- 响应注释
Source:
更简洁的方法:规范化 CRM 模型
在 Aurinko,我们正在完成对 canonical CRM API 的 MS Dynamics 支持。主要目标之一是消除开发者体验中的平台特定怪癖。
开发者不再需要处理 _ownerid_value 或 ownerid@odata.bind,只需使用:
owner.idowner.typeowner.name
Aurinko 在后台处理繁重的工作:
- 多态查找解析
- 实体类型检测
- OData 绑定逻辑
- Dynamics 命名约定
- 架构不一致性
其结果是跨 CRM 平台的干净、一致的模型,这样你的集成代码就不必了解每个单独 API 的怪癖。
最后思考
Microsoft Dynamics 是一个功能极其强大的平台,但其 API 也反映了大型企业系统的复杂性。
| 上下文 | 字段名称 |
|---|---|
| 元数据 | ownerid |
| 读取 | _ownerid_value |
| 写入 | ownerid@odata.bind |
添加多态后缀规则和注释,就很容易理解为什么开发者常常感到不堪重负。通过抽象这些细节,你可以专注于业务逻辑,而不是底层实现。
我们对 Aurinko 的目标很简单:让 CRM 集成再次变得可预测——有时这需要永远隐藏 _ownerid_value。