Whilst building a model-driven PCF control, I came across a requirement to execute multiple requests as one batch operation. Unfortunately, the current TypeScript declaration for the Web API interface for PCF doesn’t expose the execute
or executeMultiple
methods. Sure, you can use the executeMultiple
method by suppressing the type checking on the TypeScript code but I didn’t feel compelled to do that… not yet! So instead, I’ve written my own little TypeScript utility classes and methods to perform batch operations without using the client Web API method.
Here is what we are going to cover in this post:
- Discuss the benefits and scenarios of the batch operations
- Explain the anatomy of the batch request
- Walk through the utility code…?
- Code implementation of the examples from the Microsoft documentation
Why use Batch Operation?
The batch operation is required if you have multiple operations that need to be grouped and processed as one single request. One of the important features of batch operation is that it succeeds or fails as a group. This is especially useful when executing multiple write operations where if one operation in the batch fails then all the changes within the changeset will be rolled back. Another common usage of a batch operation is to increase the performance of the application by grouping all operations as one request to reduce the number of round trips between the client and the server, less “chatter”.
Anatomy of the Batch Request
A great place to start when working with batch operations in Microsoft Dataverse is this documentation here. This Microsoft documentation has lots of in-depth explanation of how to compose batch requests, change sets and a set of examples that highlight the usage of batch operations. However, as that documentation states, the batch request body is more difficult to compose because the request body is essentially a text document that must match very specific requirements.
Trying to understand the batch request when you don’t know how it is composed can be very daunting. So rather than trying to explain it in words, I’ve drawn up a little diagram:
Here are few key things to know:
- A batch operation is a POST request where the body is made up of a series of individual requests and change sets. In the above diagram, we have 2 change sets and 2 GET requests. Note: currently there is a limit of 1000 individual requests
- The change sets are made up of one or more data modification operations such as create, update and delete. A change set is a transactional operation which means if any one of these operations fail then all completed operations will be rolled back
- The boundary of the batch is set with a unique identifier prefixed by
batch_<unique identifier>
- Each individual request and change set in the batch request body must start with
--batch_<unique identifier>
- The boundary of the change set is set with a unique identifier prefixed by
changeset_<unique identifier>
- Each individual change set request operation must start with
--changeset_<unique identifier>
- End of the change set is set with
--changeset_<unique identifier>--
- End of the batch is set with
--batch_<unique identifier>--
- All requests in a batch run sequentially. This is important for referencing resources created within the same change set (more on this later)
Let’s Walk Through the Utility Code…!?
The utility code implements what we’ve discussed so far so I won’t go into further detail here. Instead, I’ve inserted comments throughout the code to highlight some of the concepts and the functionalities.
Tip - To get a better idea of how this code can be consumed, I suggest that you either skim or come back to the code after reading the next section!
Implement the Examples From the Microsoft Documentation
The examples in the Microsoft Documentation are great but there are no code examples on how to execute them. Here I’ll go through each example and have a code snippet on how to execute these batches using my utility code.
Tip - For each example I'll have a link. Open the link to the example on the Microsoft Documentation and observe the Request and Response of the Web API side-by-side with the code snippet to fully understand each batch scenario.
Example #1 – (MS link)
This batch operation is composed of:
- A change set with two POST requests that create task records for an account
- A GET request to retrieve the account record with the tasks associated with the account, including the ones just created
- A GET request to retrieve the account record with the inclusion of property annotation by using the
odata.include-annotations
preference header
The response of the batch is also parsed into an IResponse
collection so the result can be also easily consumed.
Example #2 – Reference URIs in request body (MS Link)
Something great about the batch operation is that you can reference the new entities created earlier in the same change set by using the $parameter
. This is extremely useful in a scenario where you need to set the entity references and associate records all in the same batch. $parameter
is just the value of the Content-ID
header of the request. For example, a POST request with header Content-ID: 1
, can later be referenced by another request using $1
.
Note – I switched out the lead entity with account entity in this example as I wasn’t working in the Dynamics 365 environment.
Example #3 – Reference URI in request URL – (MS Link)
Example of using $parameter
in a request URL.
Example #4 – Reference URIs in URL and request body using @odata.id – (MS Link)
Example of using $parameter
to link entity records, in this scenario, link Contact entity record to an Account entity record.
Example #5 – Reference URIs in URL and navigation properties – (MS Link)
Example of using $parameter
to link entity records to a single-value navigation property and the ability to use the full URI of the record to perform a PATCH operation.
Utility Example #6 – Error Handling
In this example, I deliberately introduced an error in one of the change set requests to illustrate that the batch operation will terminate with an error response as the last part of the multi-part response.
The IResponse
collection returns the first GET request and error from the change set:
Utility Example #7 – Multiple change sets in a batch
This example highlights that a batch can contain multiple change sets. The utility code collects and executes all the requests sequentially. It will naturally break for a boundary when a GET request is inserted but if you want to force a boundary between change set requests then you can use the forceBoundary
method.
Note - This works great in a model-driven PCF as calling those web API endpoints does not require re-authentication. I will extend this utility code later to accept authentication headers to future proof other scenarios.
That’s it! Hope you enjoyed the post, let me know your thoughts in the comments below or any of my contact channels 😊