可以通过批处理操作将多个操作组合到单个 HTTP 请求中。 Dataverse 按指定的顺序执行这些操作。 响应的顺序与批处理作中的请求顺序匹配。
发送 $batch 请求的格式在 OData 规范中的 11.7 批处理请求 中定义。 本文总结了规范要求,并提供特定于 Dataverse 的信息和示例。
何时使用批处理请求
批处理请求提供两项功能,可以结合使用:
可以使用单个 HTTP 请求发送多个操作请求。
- 批处理请求最多可以包含 1,000 个单个请求,并且不能包含其他批处理请求。
- Web API
$batch请求等效于ExecuteMultipleSDK for .NET 中提供的消息。 有关详细信息,请参阅 使用 SDK for .NET 执行多个请求。
您可以使用变更集将操作请求分组,使其作为单个事务被包含。
- 你可能想要创建、更新或删除一组相关记录,以确保所有操作要么全部成功,要么全部失败。
- 使用变更集进行的 Web API
$batch请求等同于SDK FOR .NET 中提供的ExecuteTransaction消息。 有关详细信息,请参阅 在单个数据库事务中执行消息。
注释
在单个作中创建关联的实体比使用批处理请求更容易。 有关详细信息,请参阅 在一个操作中创建相关表行。
当 URL 长度可能超过GET时,用户有时会使用批处理请求发送请求。 通过使用批处理请求,可以在消息正文中包含请求的 URL,URL 长度最多允许为 64 KB(65,536 个字符)。 使用 FetchXml 发送复杂查询可能会导致 URL 较长。 有关详细信息,请参阅 在批处理请求中使用 FetchXML。
与可以使用 Web API 执行的其他作相比,批处理请求更难撰写。 原始请求和响应正文本质上是必须符合特定要求的文本文档。 若要访问响应中的数据,需要分析响应中的文本,或找到帮助程序库以访问响应中的数据。 请参阅 .NET 帮助程序方法。
批处理请求
使用POST请求提交包含多个请求的批处理操作。
POST请求中包含批处理时,必须具有一个 Content-Type 标头,其值应设置为multipart/mixed,并且boundary应使用以下模式包含批处理的标识符:
POST [Organization Uri]/api/data/v9.2/$batch HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Accept: application/json
Content-Type: multipart/mixed; boundary="batch_<unique identifier>"
唯一标识符不需要是 GUID,但它应是唯一的。
批中的每个项都必须前面有一个 Content-Type 和 Content-Transfer-Encoding 标头的批处理标识符,如以下示例所示:
--batch_<unique identifier>
Content-Type: application/http
Content-Transfer-Encoding: binary
重要
仅会执行那些批处理标识符与 Content-Type 标头中发送的批处理标识符匹配的负载项。 如果没有有效负载项使用 Content-Type 批处理标识符,则批处理请求会成功,而无需执行任何有效负载项。
您必须为批处理中的每个项包含其他 HTTP 标头,以控制该请求的行为。 应用于$batch操作的标头不适用于每个项目。 例如,如果包含请求 GET 并想要 请求批注,则必须将相应的 Prefer: odata.include-annotations="*" 标头添加到每个项。
批处理请求的结束必须包含终止指示器,如以下示例所示:
--batch_<unique identifier>--
注释
HTTP 协议要求$batch请求有效负载的所有行尾都为 CRLF。
不同的行尾符可能会导致反序列化错误。 例如: System.ArgumentException: Stream was not readable.。 如果不能使用CRLF,可以在请求负载的末尾添加两个非CRLF的行结束符,以解决大多数反序列化错误。
以下示例是没有更改集的批处理请求。 本示例:
- 创建三个与账户关联的任务记录,其中
accountid等于00000000-0000-0000-0000-000000000001。 - 检索与帐户关联的任务记录。
请求:
POST [Organization Uri]/api/data/v9.2/$batch HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Accept: application/json
Content-Type: multipart/mixed; boundary="batch_80dd1615-2a10-428a-bb6f-0e559792721f"
--batch_80dd1615-2a10-428a-bb6f-0e559792721f
Content-Type: application/http
Content-Transfer-Encoding: binary
POST /api/data/v9.2/tasks HTTP/1.1
Content-Type: application/json; type=entry
{
"subject": "Task 1 in batch",
"regardingobjectid_account_task@odata.bind": "accounts(00000000-0000-0000-0000-000000000001)"
}
--batch_80dd1615-2a10-428a-bb6f-0e559792721f
Content-Type: application/http
Content-Transfer-Encoding: binary
POST /api/data/v9.2/tasks HTTP/1.1
Content-Type: application/json; type=entry
{
"subject": "Task 2 in batch",
"regardingobjectid_account_task@odata.bind": "accounts(00000000-0000-0000-0000-000000000001)"
}
--batch_80dd1615-2a10-428a-bb6f-0e559792721f
Content-Type: application/http
Content-Transfer-Encoding: binary
POST /api/data/v9.2/tasks HTTP/1.1
Content-Type: application/json; type=entry
{
"subject": "Task 3 in batch",
"regardingobjectid_account_task@odata.bind": "accounts(00000000-0000-0000-0000-000000000001)"
}
--batch_80dd1615-2a10-428a-bb6f-0e559792721f
Content-Type: application/http
Content-Transfer-Encoding: binary
GET /api/data/v9.2/accounts(00000000-0000-0000-0000-000000000001)/Account_Tasks?$select=subject HTTP/1.1
--batch_80dd1615-2a10-428a-bb6f-0e559792721f--
批处理响应
成功后,批处理响应将返回 HTTP 状态 200 OK。 响应中的每个项都由与批处理请求值不相同的唯一 Guid 标识符值分隔。
--batchresponse_<unique identifier>
Content-Type: application/http
Content-Transfer-Encoding: binary
批处理响应的末尾包含终止指示器,如以下示例所示:
--batchresponse_<unique identifier>--
以下示例显示了对上一批请求示例的响应。
响应:
HTTP/1.1 200 OK
OData-Version: 4.0
--batchresponse_01346794-f2e2-4d45-8cc2-f97e09fe8916
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 204 No Content
OData-Version: 4.0
Location: [Organization Uri]/api/data/v9.2/tasks(00aa00aa-bb11-cc22-dd33-44ee44ee44ee)
OData-EntityId: [Organization Uri]/api/data/v9.2/tasks(00aa00aa-bb11-cc22-dd33-44ee44ee44ee)
--batchresponse_01346794-f2e2-4d45-8cc2-f97e09fe8916
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 204 No Content
OData-Version: 4.0
Location: [Organization Uri]/api/data/v9.2/tasks(11bb11bb-cc22-dd33-ee44-55ff55ff55ff)
OData-EntityId: [Organization Uri]/api/data/v9.2/tasks(11bb11bb-cc22-dd33-ee44-55ff55ff55ff)
--batchresponse_01346794-f2e2-4d45-8cc2-f97e09fe8916
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 204 No Content
OData-Version: 4.0
Location: [Organization Uri]/api/data/v9.2/tasks(22cc22cc-dd33-ee44-ff55-66aa66aa66aa)
OData-EntityId: [Organization Uri]/api/data/v9.2/tasks(22cc22cc-dd33-ee44-ff55-66aa66aa66aa)
--batchresponse_01346794-f2e2-4d45-8cc2-f97e09fe8916
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
{
"@odata.context": "[Organization Uri]/api/data/v9.2/$metadata#tasks(subject)",
"value": [
{
"@odata.etag": "W/\"77180907\"",
"subject": "Task 1 in batch",
"activityid": "00aa00aa-bb11-cc22-dd33-44ee44ee44ee"
},
{
"@odata.etag": "W/\"77180910\"",
"subject": "Task 2 in batch",
"activityid": "11bb11bb-cc22-dd33-ee44-55ff55ff55ff"
},
{
"@odata.etag": "W/\"77180913\"",
"subject": "Task 3 in batch",
"activityid": "22cc22cc-dd33-ee44-ff55-66aa66aa66aa"
}
]
}
--batchresponse_01346794-f2e2-4d45-8cc2-f97e09fe8916--
更改集
除了单个请求,批量请求还可以包括更改集。 当一个更改集包含多个操作时,所有操作都被视为原子性。 原子操作意味着,如果任一操作失败,批处理请求将回滚任何已完成的操作。
注释
GET 不允许在更改集中进行请求。
GET 操作不会更改数据,因此它不属于变更集。
与批量请求类似,变更集必须包含一个 Content-Type 标头,其值设置为 multipart/mixed,并设置 boundary 以包含变更集的标识符,使用以下模式:
Content-Type: multipart/mixed; boundary="changeset_<unique identifier>"
唯一标识符不需要是 GUID,但它应是唯一的。 变更集中的每个项目前都必须带有变更集标识符,并包含 Content-Type 和 Content-Transfer-Encoding 标头,如下例所示:
--changeset_<unique identifier>
Content-Type: application/http
Content-Transfer-Encoding: binary
更改集还可以包含具有唯一 Content-ID 值的标头。 此值在变更集的后续部分中将以 , represents a variable that contains the URI for any entity created in that operation. For example, when you set the value of 1, you can refer to that entity by using $1` 为前缀。 若要了解详细信息,请参阅 作中的参考 URI。
更改集的末尾必须包含如下例所示的终止指示器:
--changeset_<unique identifier>--
以下示例展示了如何使用变更集:
- 将与
accountid值为00000000-0000-0000-0000-000000000001的账户相关的三个任务的创建操作进行分组。 - 通过在变更集外部使用
GET请求来检索已创建的账户。
请求:
POST [Organization Uri]/api/data/v9.2/$batch HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Accept: application/json
Content-Type: multipart/mixed; boundary="batch_22975cad-7f57-410d-be15-6363209367ea"
--batch_22975cad-7f57-410d-be15-6363209367ea
Content-Type: multipart/mixed; boundary="changeset_246e6bfe-89a4-4c77-b293-7a433f082e8a"
--changeset_246e6bfe-89a4-4c77-b293-7a433f082e8a
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
POST /api/data/v9.2/tasks HTTP/1.1
Content-Type: application/json; type=entry
{
"subject": "Task 1 in batch",
"regardingobjectid_account_task@odata.bind": "accounts(00000000-0000-0000-0000-000000000001)"
}
--changeset_246e6bfe-89a4-4c77-b293-7a433f082e8a
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
POST /api/data/v9.2/tasks HTTP/1.1
Content-Type: application/json; type=entry
{
"subject": "Task 2 in batch",
"regardingobjectid_account_task@odata.bind": "accounts(00000000-0000-0000-0000-000000000001)"
}
--changeset_246e6bfe-89a4-4c77-b293-7a433f082e8a
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3
POST /api/data/v9.2/tasks HTTP/1.1
Content-Type: application/json; type=entry
{
"subject": "Task 3 in batch",
"regardingobjectid_account_task@odata.bind": "accounts(00000000-0000-0000-0000-000000000001)"
}
--changeset_246e6bfe-89a4-4c77-b293-7a433f082e8a--
--batch_22975cad-7f57-410d-be15-6363209367ea
Content-Type: application/http
Content-Transfer-Encoding: binary
GET /api/data/v9.2/accounts(00000000-0000-0000-0000-000000000001)/Account_Tasks?$select=subject HTTP/1.1
--batch_22975cad-7f57-410d-be15-6363209367ea--
响应:
HTTP/1.1 200 OK
OData-Version: 4.0
--batchresponse_f27ef42d-51b0-4685-bac9-f468f844de2f
Content-Type: multipart/mixed; boundary=changesetresponse_64cc3fff-023a-45b0-b29d-df21583ffa15
--changesetresponse_64cc3fff-023a-45b0-b29d-df21583ffa15
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
HTTP/1.1 204 No Content
OData-Version: 4.0
Location: [Organization Uri]/api/data/v9.2/tasks(33dd33dd-ee44-ff55-aa66-77bb77bb77bb)
OData-EntityId: [Organization Uri]/api/data/v9.2/tasks(33dd33dd-ee44-ff55-aa66-77bb77bb77bb)
--changesetresponse_64cc3fff-023a-45b0-b29d-df21583ffa15
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
HTTP/1.1 204 No Content
OData-Version: 4.0
Location: [Organization Uri]/api/data/v9.2/tasks(44ee44ee-ff55-aa66-bb77-88cc88cc88cc)
OData-EntityId: [Organization Uri]/api/data/v9.2/tasks(44ee44ee-ff55-aa66-bb77-88cc88cc88cc)
--changesetresponse_64cc3fff-023a-45b0-b29d-df21583ffa15
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3
HTTP/1.1 204 No Content
OData-Version: 4.0
Location: [Organization Uri]/api/data/v9.2/tasks(55ff55ff-aa66-bb77-cc88-99dd99dd99dd)
OData-EntityId: [Organization Uri]/api/data/v9.2/tasks(55ff55ff-aa66-bb77-cc88-99dd99dd99dd)
--changesetresponse_64cc3fff-023a-45b0-b29d-df21583ffa15--
--batchresponse_f27ef42d-51b0-4685-bac9-f468f844de2f
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
{
"@odata.context": "[Organization Uri]/api/data/v9.2/$metadata#tasks(subject)",
"value": [
{
"@odata.etag": "W/\"77181173\"",
"subject": "Task 1 in batch",
"activityid": "33dd33dd-ee44-ff55-aa66-77bb77bb77bb"
},
{
"@odata.etag": "W/\"77181176\"",
"subject": "Task 2 in batch",
"activityid": "44ee44ee-ff55-aa66-bb77-88cc88cc88cc"
},
{
"@odata.etag": "W/\"77181179\"",
"subject": "Task 3 in batch",
"activityid": "55ff55ff-aa66-bb77-cc88-99dd99dd99dd"
}
]
}
--batchresponse_f27ef42d-51b0-4685-bac9-f468f844de2f--
操作中的引用 URI
在变更集中,您可以使用 $parameter(如 $1、$2 等)来引用同一变更集中先前创建的新实体的返回 URI。 有关详细信息,请参阅 OData v4.0 规范:11.7.3.1 引用变更集中的请求。
本部分演示了如何在批处理作的请求正文中使用 $parameter 以引用 URI 的各种示例。
请求正文中的引用 URI
以下示例演示如何在单个作中使用两个 URI 引用。
请求:
POST [Organization URI]/api/data/v9.2/$batch HTTP/1.1
Content-Type: multipart/mixed;boundary=batch_AAA123
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
--batch_AAA123
Content-Type: multipart/mixed; boundary=changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
POST [Organization URI]/api/data/v9.2/leads HTTP/1.1
Content-Type: application/json
{
"firstname":"first name",
"lastname":"last name"
}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
POST [Organization URI]/api/data/v9.2/contacts HTTP/1.1
Content-Type: application/json
{"firstname":"first name"}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3
POST [Organization URI]/api/data/v9.2/accounts HTTP/1.1
Content-Type: application/json
{
"name":"IcM Account",
"originatingleadid@odata.bind":"$1",
"primarycontactid@odata.bind":"$2"
}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab--
--batch_AAA123--
响应:
HTTP/1.1 200 OK
OData-Version: 4.0
--batchresponse_3cace264-86ea-40fe-83d3-954b336c0f4a
Content-Type: multipart/mixed; boundary=changesetresponse_1a5db8a1-ec98-42c4-81f6-6bc6adcfa4bc
--changesetresponse_1a5db8a1-ec98-42c4-81f6-6bc6adcfa4bc
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
HTTP/1.1 204 No Content
OData-Version: 4.0
Location: [Organization URI]/api/data/v9.2/leads(66aa66aa-bb77-cc88-dd99-00ee00ee00ee)
OData-EntityId: [Organization URI]/api/data/v9.2/leads(66aa66aa-bb77-cc88-dd99-00ee00ee00ee)
--changesetresponse_1a5db8a1-ec98-42c4-81f6-6bc6adcfa4bc
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
HTTP/1.1 204 No Content
OData-Version: 4.0
Location: [Organization URI]/api/data/v9.2/contacts(00aa00aa-bb11-cc22-dd33-44ee44ee44ee)
OData-EntityId: [Organization URI]/api/data/v9.2/contacts(00aa00aa-bb11-cc22-dd33-44ee44ee44ee)
--changesetresponse_1a5db8a1-ec98-42c4-81f6-6bc6adcfa4bc
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3
HTTP/1.1 204 No Content
OData-Version: 4.0
Location: [Organization URI]/api/data/v9.2/accounts(11bb11bb-cc22-dd33-ee44-55ff55ff55ff)
OData-EntityId: [Organization URI]/api/data/v9.2/accounts(11bb11bb-cc22-dd33-ee44-55ff55ff55ff)
--changesetresponse_1a5db8a1-ec98-42c4-81f6-6bc6adcfa4bc--
--batchresponse_3cace264-86ea-40fe-83d3-954b336c0f4a--
请求 URL 中的引用 URI
以下示例演示如何在后续请求的 URL 中使用 $1 来引用 URI。
请求:
POST [Organization URI]/api/data/v9.2/$batch HTTP/1.1
Content-Type: multipart/mixed;boundary=batch_AAA123
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
--batch_AAA123
Content-Type: multipart/mixed; boundary=changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
POST [Organization URI]/api/data/v9.2/contacts HTTP/1.1
Content-Type: application/json
{
"firstname":"First Name",
"lastname":"Last name"
}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Transfer-Encoding: binary
Content-Type: application/http
Content-ID: 2
PUT $1/lastname HTTP/1.1
Content-Type: application/json
{
"value":"BBBBB"
}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab--
--batch_AAA123--
响应:
HTTP/1.1 200 OK
OData-Version: 4.0
--batchresponse_2cb48f48-39a8-41ea-aa52-132fa8ab3c2d
Content-Type: multipart/mixed; boundary=changesetresponse_d7528170-3ef3-41bd-be8e-eac971a8d9d4
--changesetresponse_d7528170-3ef3-41bd-be8e-eac971a8d9d4
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
HTTP/1.1 204 No Content
OData-Version: 4.0
Location:[Organization URI]/api/data/v9.2/contacts(22cc22cc-dd33-ee44-ff55-66aa66aa66aa)
OData-EntityId:[Organization URI]/api/data/v9.2/contacts(22cc22cc-dd33-ee44-ff55-66aa66aa66aa)
--changesetresponse_d7528170-3ef3-41bd-be8e-eac971a8d9d4
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
HTTP/1.1 204 No Content
OData-Version: 4.0
--changesetresponse_d7528170-3ef3-41bd-be8e-eac971a8d9d4--
--batchresponse_2cb48f48-39a8-41ea-aa52-132fa8ab3c2d--
使用 @odata.id 引用 URL 和请求正文中的 URI
以下示例演示如何将联系人实体记录链接到帐户实体记录。 帐户实体记录的 URI 被引用为$1,联系人实体记录的 URI 被引用为$2。
请求:
POST [Organization URI]/api/data/v9.2/$batch HTTP/1.1
Content-Type:multipart/mixed;boundary=batch_AAA123
Accept:application/json
OData-MaxVersion:4.0
OData-Version:4.0
--batch_AAA123
Content-Type: multipart/mixed; boundary=changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type:application/http
Content-Transfer-Encoding:binary
Content-ID:1
POST [Organization URI]/api/data/v9.2/accounts HTTP/1.1
Content-Type: application/json
{ "name":"Account Name"}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type:application/http
Content-Transfer-Encoding:binary
Content-ID:2
POST [Organization URI]/api/data/v9.2/contacts HTTP/1.1
Content-Type:application/json
{ "firstname":"Contact first name"}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type:application/http
Content-Transfer-Encoding:binary
Content-ID:3
PUT $1/primarycontactid/$ref HTTP/1.1
Content-Type:application/json
{"@odata.id":"$2"}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab--
--batch_AAA123--
响应:
HTTP/1.1 200 OK
OData-Version: 4.0
--batchresponse_0740a25c-d8e1-41a5-9202-1b50a297864c
Content-Type: multipart/mixed; boundary=changesetresponse_19ca0da8-d8bb-4273-a3f7-fe0d0fadfe5f
--changesetresponse_19ca0da8-d8bb-4273-a3f7-fe0d0fadfe5f
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
HTTP/1.1 204 No Content
OData-Version: 4.0
Location:[Organization URI]/api/data/v9.2/accounts(33dd33dd-ee44-ff55-aa66-77bb77bb77bb)
OData-EntityId:[Organization URI]/api/data/v9.2/accounts(33dd33dd-ee44-ff55-aa66-77bb77bb77bb)
--changesetresponse_19ca0da8-d8bb-4273-a3f7-fe0d0fadfe5f
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
HTTP/1.1 204 No Content
OData-Version: 4.0
Location:[Organization URI]/api/data/v9.2/contacts(44ee44ee-ff55-aa66-bb77-88cc88cc88cc)
OData-EntityId:[Organization URI]/api/data/v9.2/contacts(44ee44ee-ff55-aa66-bb77-88cc88cc88cc)
--changesetresponse_19ca0da8-d8bb-4273-a3f7-fe0d0fadfe5f
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3
HTTP/1.1 204 No Content
OData-Version: 4.0
--changesetresponse_19ca0da8-d8bb-4273-a3f7-fe0d0fadfe5f--
--batchresponse_0740a25c-d8e1-41a5-9202-1b50a297864c--
URL 和导航属性中的引用 URI
以下示例演示如何使用联系人记录的组织 URI,并使用单值导航属性将其链接到帐户记录 primarycontactid 。 在 PATCH 请求中,账户实体记录的 URI 被引用为 $1,联系人实体记录的 URI 被引用为 $2。
请求:
POST [Organization URI]/api/data/v9.2/$batch HTTP/1.1
Content-Type:multipart/mixed;boundary=batch_AAA123
Accept:application/json
OData-MaxVersion:4.0
OData-Version:4.0
--batch_AAA123
Content-Type: multipart/mixed; boundary=changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
POST [Organization URI]/api/data/v9.2/accounts HTTP/1.1
Content-Type: application/json
{ "name":"Account name"}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
POST [Organization URI]/api/data/v9.2/contacts HTTP/1.1
Content-Type: application/json
{
"firstname":"Contact first name"
}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3
PATCH $1 HTTP/1.1
Content-Type: application/json
{
"primarycontactid@odata.bind":"$2"
}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab--
--batch_AAA123--
响应:
HTTP/1.1 200 OK
OData-Version: 4.0
--batchresponse_9595d3ae-48f6-414f-a3aa-a3a33559859e
Content-Type: multipart/mixed; boundary=changesetresponse_0c1567a5-ad0d-48fa-b81d-e6db05cad01c
--changesetresponse_0c1567a5-ad0d-48fa-b81d-e6db05cad01c
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
HTTP/1.1 204 No Content
OData-Version: 4.0
Location: [Organization URI]/api/data/v9.2/accounts(55ff55ff-aa66-bb77-cc88-99dd99dd99dd)
OData-EntityId: [Organization URI]/api/data/v9.2/accounts(55ff55ff-aa66-bb77-cc88-99dd99dd99dd)
--changesetresponse_0c1567a5-ad0d-48fa-b81d-e6db05cad01c
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
HTTP/1.1 204 No Content
OData-Version: 4.0
Location: [Organization URI]/api/data/v9.2/contacts(66aa66aa-bb77-cc88-dd99-00ee00ee00ee)
OData-EntityId: [Organization URI]/api/data/v9.2/contacts(66aa66aa-bb77-cc88-dd99-00ee00ee00ee)
--changesetresponse_0c1567a5-ad0d-48fa-b81d-e6db05cad01c
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3
HTTP/1.1 204 No Content
OData-Version: 4.0
Location: [Organization URI]/api/data/v9.2/accounts(55ff55ff-aa66-bb77-cc88-99dd99dd99dd)
OData-EntityId: [Organization URI]/api/data/v9.2/accounts(55ff55ff-aa66-bb77-cc88-99dd99dd99dd)
--changesetresponse_0c1567a5-ad0d-48fa-b81d-e6db05cad01c--
--batchresponse_9595d3ae-48f6-414f-a3aa-a3a33559859e--
注释
在请求正文中出现之前引用Content-ID会导致返回HTTP 400错误请求。
以下示例显示了可能导致此错误的请求正文。
请求正文
--batch_AAA123
Content-Type: multipart/mixed; boundary=changeset_BBB456
--changeset_BBB456
Content-Type: application/http
Content-Transfer-Encoding:binary
Content-ID: 2
POST [Organization URI]/api/data/v9.2/phonecalls HTTP/1.1
Content-Type: application/json;type=entry
{
"phonenumber":"911",
"regardingobjectid_account_phonecall@odata.bind" : "$1"
}
--changeset_BBB456
Content-Type: application/http
Content-Transfer-Encoding:binary
Content-ID: 1
POST [Organization URI]/api/data/v9.2/accounts HTTP/1.1
Content-Type: application/json;type=entry
{
"name":"QQQQ",
"revenue": 1.50
}
--changeset_BBB456--
--batch_AAA123--
响应:
HTTP 400 Bad Request
Content-ID Reference: '$1' does not exist in the batch context.
处理错误
当批处理中某个请求出错时,批处理请求将返回该请求的错误,并且不会处理任何其他请求。
如果添加 Prefer: odata.continue-on-error 请求标头,可以指定服务器在发生错误时处理更多请求。 批处理请求返回 200 OK,单个响应错误包含在批处理响应正文中。
更多信息:OData 规范:8.2.8.3 首选项 odata.continue-on-error
示例
以下示例尝试为一个 accountid 等于 00000000-0000-0000-0000-000000000001 的账户创建三个任务记录,但第一个任务的 subject 属性长度过长。
请求:
POST [Organization Uri]/api/data/v9.2/$batch HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Accept: application/json
Content-Type: multipart/mixed; boundary="batch_431faf5a-f979-4ee6-a374-d242f8962d41"
Content-Length: 1335
--batch_431faf5a-f979-4ee6-a374-d242f8962d41
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-Length: 436
POST /api/data/v9.2/tasks HTTP/1.1
Content-Type: application/json; type=entry
{
"subject": "Subject is too long xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"regardingobjectid_account_task@odata.bind": "accounts(00000000-0000-0000-0000-000000000001)"
}
--batch_431faf5a-f979-4ee6-a374-d242f8962d41
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-Length: 250
POST /api/data/v9.2/tasks HTTP/1.1
Content-Type: application/json; type=entry
{
"subject": "Task 2 in batch",
"regardingobjectid_account_task@odata.bind": "accounts(00000000-0000-0000-0000-000000000001)"
}
--batch_431faf5a-f979-4ee6-a374-d242f8962d41
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-Length: 250
POST /api/data/v9.2/tasks HTTP/1.1
Content-Type: application/json; type=entry
{
"subject": "Task 3 in batch",
"regardingobjectid_account_task@odata.bind": "accounts(00000000-0000-0000-0000-000000000001)"
}
--batch_431faf5a-f979-4ee6-a374-d242f8962d41--
如果不设置 Prefer: odata.continue-on-error 请求标头,批处理在批处理中的第一个请求上会失败。 批处理错误表示第一个失败请求的错误。
响应:
HTTP/1.1 400 BadRequest
OData-Version: 4.0
--batchresponse_156da4b8-cd2c-4862-a911-4aaab97c001a
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 400 Bad Request
REQ_ID: 5ecd1cb3-1730-4ffc-909c-d44c22270026
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{"error":{"code":"0x80044331","message":"A validation error occurred. The length of the 'subject' attribute of the 'task' entity exceeded the maximum allowed length of '200'."}}
--batchresponse_156da4b8-cd2c-4862-a911-4aaab97c001a--
将 Prefer: odata.continue-on-error 请求标头添加到批处理请求时,批处理请求会以200 OK状态成功,第一个请求的失败作为正文的一部分返回。
请求:
POST [Organization Uri]/api/data/v9.2/$batch HTTP/1.1
Prefer: odata.continue-on-error
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Accept: application/json
Content-Type: multipart/mixed; boundary="batch_662d4610-7f12-4895-ac4a-3fdf77cc10a1"
Content-Length: 1338
--batch_662d4610-7f12-4895-ac4a-3fdf77cc10a1
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-Length: 439
POST /api/data/v9.2/tasks HTTP/1.1
Content-Type: application/json; type=entry
{
"subject": "Subject is too long xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"regardingobjectid_account_task@odata.bind": "accounts(00000000-0000-0000-0000-000000000001)"
}
--batch_662d4610-7f12-4895-ac4a-3fdf77cc10a1
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-Length: 250
POST /api/data/v9.2/tasks HTTP/1.1
Content-Type: application/json; type=entry
{
"subject": "Task 2 in batch",
"regardingobjectid_account_task@odata.bind": "accounts(00000000-0000-0000-0000-000000000001)"
}
--batch_662d4610-7f12-4895-ac4a-3fdf77cc10a1
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-Length: 250
POST /api/data/v9.2/tasks HTTP/1.1
Content-Type: application/json; type=entry
{
"subject": "Task 3 in batch",
"regardingobjectid_account_task@odata.bind": "accounts(00000000-0000-0000-0000-000000000001)"
}
--batch_662d4610-7f12-4895-ac4a-3fdf77cc10a1--
响应:
HTTP/1.1 200 OK
OData-Version: 4.0
--batchresponse_f44bd09d-573f-4a30-bca0-2e500ee7e139
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 400 Bad Request
REQ_ID: de4c5227-4a28-4ebd-8ced-3392ece1697b
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{"error":{"code":"0x80044331","message":"A validation error occurred. The length of the 'subject' attribute of the 'task' entity exceeded the maximum allowed length of '200'."}}
--batchresponse_f44bd09d-573f-4a30-bca0-2e500ee7e139
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 204 No Content
OData-Version: 4.0
Location: [Organization Uri]/api/data/v9.2/tasks(00aa00aa-bb11-cc22-dd33-44ee44ee44ee)
OData-EntityId: [Organization Uri]/api/data/v9.2/tasks(00aa00aa-bb11-cc22-dd33-44ee44ee44ee)
--batchresponse_f44bd09d-573f-4a30-bca0-2e500ee7e139
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 204 No Content
OData-Version: 4.0
Location: [Organization Uri]/api/data/v9.2/tasks(11bb11bb-cc22-dd33-ee44-55ff55ff55ff)
OData-EntityId: [Organization Uri]/api/data/v9.2/tasks(11bb11bb-cc22-dd33-ee44-55ff55ff55ff)
--batchresponse_f44bd09d-573f-4a30-bca0-2e500ee7e139--
.NET 辅助方法
WebAPIService 类库 (C#) 是一个示例帮助程序类库项目,用于使用 .NET 编写的 Web API 示例。 它演示了重用 Web API 中使用的常见模式的一种方法。
注释
此示例库是所有 Dataverse C# Web API 示例使用的帮助程序,但它不是 SDK。 仅测试用于确认使用该数据的示例能否成功运行。 此示例代码提供“as-is”,不保证重复使用。
此库包括用于创建批处理请求和处理响应的类。 例如,以下代码的变体用于生成本文中的许多 HTTP 请求和响应示例。
using PowerApps.Samples;
using PowerApps.Samples.Batch;
static async Task Main()
{
Config config = App.InitializeApp();
var service = new Service(config);
JObject account = new()
{
{"name","test account" }
};
EntityReference accountRef = await service.Create("accounts", account);
List<HttpRequestMessage> createRequests = new() {
new CreateRequest("tasks",new JObject(){
{"subject","Task 2 in batch" },
{"regardingobjectid_account_task@odata.bind", accountRef.Path }
}),
new CreateRequest("tasks",new JObject(){
{"subject","Task 2 in batch" },
{"regardingobjectid_account_task@odata.bind", accountRef.Path }
}),
new CreateRequest("tasks",new JObject(){
{"subject","Task 3 in batch" },
{"regardingobjectid_account_task@odata.bind", accountRef.Path }
})
};
BatchRequest batchRequest = new(service.BaseAddress)
{
Requests = createRequests,
ContinueOnError = true
};
var batchResponse = await service.SendAsync<BatchResponse>(batchRequest);
batchResponse.HttpResponseMessages.ForEach(response => {
string path = response.As<CreateResponse>().EntityReference.Path;
Console.WriteLine($"Task created at: {path}");
});
}
输出
Task created at: tasks(6743adfa-4a94-ed11-aad1-000d3a9933c9)
Task created at: tasks(6843adfa-4a94-ed11-aad1-000d3a9933c9)
Task created at: tasks(6943adfa-4a94-ed11-aad1-000d3a9933c9)
在此库中,你可能会发现一些对 .NET 代码有用的方法。
详细信息:
.NET 中 HttpRequestMessage 转换为 HttpMessageContent 的示例
在 .NET 中,必须将批处理请求 MultipartContent作为 HttpContent 的集合发送。
HttpMessageContent 继承自 HttpContent.
WebAPIService 类库 (C#)BatchRequest 类使用以下私有静态 ToMessageContent 方法将 HttpRequestMessage 转换为 HttpMessageContent,您可以将其添加到 MultipartContent 中。
/// <summary>
/// Converts a HttpRequestMessage to HttpMessageContent
/// </summary>
/// <param name="request">The HttpRequestMessage to convert.</param>
/// <returns>HttpMessageContent with the correct headers.</returns>
private HttpMessageContent ToMessageContent(HttpRequestMessage request)
{
//Relative URI is not allowed with MultipartContent
request.RequestUri = new Uri(
baseUri: ServiceBaseAddress,
relativeUri: request.RequestUri.ToString());
if (request.Content != null)
{
if (request.Content.Headers.Contains("Content-Type"))
{
request.Content.Headers.Remove("Content-Type");
}
request.Content.Headers.Add("Content-Type", "application/json;type=entry");
}
HttpMessageContent messageContent = new(request);
if (messageContent.Headers.Contains("Content-Type"))
{
messageContent.Headers.Remove("Content-Type");
}
messageContent.Headers.Add("Content-Type", "application/http");
messageContent.Headers.Add("Content-Transfer-Encoding", "binary");
return messageContent;
}
.NET 解析批处理响应示例
WebAPIService 类库 (C#)BatchResponse 类使用以下专用静态ParseMultipartContent方法将批处理响应的正文分析为List可以像单个响应一样处理的 HttpResponseMessage。
/// <summary>
/// Processes the Multi-part content returned from the batch into a list of responses.
/// </summary>
/// <param name="content">The Content of the response.</param>
/// <returns></returns>
private static async Task<List<HttpResponseMessage>> ParseMultipartContent(HttpContent content)
{
MultipartMemoryStreamProvider batchResponseContent = await content.ReadAsMultipartAsync();
List<HttpResponseMessage> responses = new();
if (batchResponseContent?.Contents != null)
{
batchResponseContent.Contents.ToList().ForEach(async httpContent =>
{
//This is true for changesets
if (httpContent.IsMimeMultipartContent())
{
//Recursive call
responses.AddRange(await ParseMultipartContent(httpContent));
}
//This is for individual responses outside of a change set.
else
{
//Must change Content-Type for ReadAsHttpResponseMessageAsync method to work.
httpContent.Headers.Remove("Content-Type");
httpContent.Headers.Add("Content-Type", "application/http;msgtype=response");
HttpResponseMessage httpResponseMessage = await httpContent.ReadAsHttpResponseMessageAsync();
if (httpResponseMessage != null)
{
responses.Add(httpResponseMessage);
}
}
});
}
return responses;
}