Hello! Welcome back to our advanced Power Platform developer course.
In our last lesson, we focused on boosting performance by processing large volumes of data with ExecuteMultipleRequest. We learned that its primary goal is throughput, and each operation within the batch runs independently in its own transaction.
Today, we're tackling the opposite requirement. What if you have a set of operations that must be treated as a single, indivisible unit? This lesson is all about ensuring data consistency. You will learn how to ensure data consistency across multiple operations using ExecuteTransactionRequest. This "all or nothing" principle is fundamental to building reliable business applications, and mastering it is a key skill for a senior developer.
1. From Performance to Consistency: The Need for Transactions
Imagine a common business scenario: when a new sales Order is created, you must also create several related Order Line records.
- What happens if the
Orderrecord is created successfully, but the creation of one of theOrder Linerecords fails due to invalid data? - You would be left with an incomplete order in your system—an orphaned
Orderheader without its required details. This is a classic data integrity problem.
In these situations, you need to ensure that the entire sequence of operations either succeeds completely or fails completely, leaving the system untouched. This is the concept of a database transaction. Given your experience with SQL, you can think of this as the Dataverse equivalent of wrapping a series of SQL statements in a BEGIN TRANSACTION...COMMIT/ROLLBACK block.
ExecuteTransactionRequest is the message that brings this transactional capability to the Dataverse service layer.
2. Understanding ExecuteTransactionRequest
ExecuteTransactionRequest acts as a container for two or more OrganizationRequest messages. When you execute it, Dataverse wraps all the contained requests within a single database transaction. If any one of the requests fails, the entire transaction is automatically rolled back, and any data changes made by preceding requests in the batch are undone.
Let's look at the official documentation to see how this is structured.
Execute messages in a single database transaction
The Microsoft Learn documentation provides the definitive explanation of this message. It outlines its purpose and structure.
Please read the introduction and the main body of the article. Focus on: The 'all or nothing' principle. How the Requests collection is used. The effect of a single request failing on the entire transaction. The ExecuteTransactionFault that is returned on failure.
As the documentation highlights, the key points are:
- Atomicity: All operations succeed, or none do.
- Sequential Execution: Requests are executed in the order they appear in the
Requestscollection. - Rollback on Failure: If request #3 in a set of 5 fails, the changes made by requests #1 and #2 are automatically reverted.
3. Implementing a Transactional Operation in C#
The implementation pattern for ExecuteTransactionRequest will feel familiar after our last lesson, but the outcome is fundamentally different. Let's see it in action.
The following code example demonstrates creating multiple records. Let's imagine these are a primary record and its dependent children that must be created together.
How to execute messages within a single database transaction
This article from CRM Crate provides a clear, practical code example that shows both a successful transaction and, more importantly, a failed transaction that triggers a rollback.
First, review the C# code snippet that prepares and executes the ExecuteTransactionRequest. Then, pay close attention to the second part where the author intentionally causes a failure by passing an invalid OptionSet value. Compare the output of the successful run to the output of the failed run to see the rollback in action.
Code Structure and Failure Handling
As you saw in the examples, the core C# structure is:
- Create an
OrganizationRequestCollectionand populate it with the requests you want to execute transactionally (e.g.,CreateRequest,UpdateRequest,DeleteRequest). - Create an
ExecuteTransactionRequestand assign your collection to itsRequestsproperty. - Execute the request inside a
try-catchblock. - Handle the success response by casting the result to
ExecuteTransactionResponseand processing theResponsescollection if needed. - Handle failures by catching a
FaultException<OrganizationServiceFault>.
When a transaction fails, the Detail property of the exception will be of type ExecuteTransactionFault. This is crucial for debugging. This fault object contains a property, FaultedRequestIndex, which tells you exactly which request in your collection (by its zero-based index) caused the failure.
// Example try-catch block for ExecuteTransactionRequest
try
{
// Execute the request...
var response = (ExecuteTransactionResponse)service.Execute(transactionRequest);
// Process successful responses...
Console.WriteLine("Transaction successful. All records were created.");
}
catch (FaultException<OrganizationServiceFault> ex)
{
// Check if the fault is from our transaction
if (ex.Detail is ExecuteTransactionFault transactionFault)
{
// Get the index of the request that failed
int failedRequestIndex = transactionFault.FaultedRequestIndex;
string failedRequestName = transactionRequest.Requests[failedRequestIndex].RequestName;
Console.WriteLine($"Transaction rolled back. The operation failed at request #{failedRequestIndex + 1} ({failedRequestName}).");
Console.WriteLine($"Reason: {ex.Detail.Message}");
}
else
{
// Handle other service faults
Console.WriteLine($"An unexpected error occurred: {ex.Message}");
}
}
Test your understanding!
You are designing a process to create a new Invoice record and its related Invoice Line records in Dataverse. It is critical that an invoice is never created without its corresponding lines. If any of the Invoice Line records fail to be created, the parent Invoice record should also not be created.
Which message should you use (ExecuteTransactionRequest or ExecuteMultipleRequest) and why?
Show answer
You should use ExecuteTransactionRequest.
The key requirement is "all or nothing" data consistency. If the creation of any invoice line fails, the entire operation, including the creation of the parent invoice, must be rolled back to prevent orphaned data. ExecuteMultipleRequest would not provide this guarantee; it could successfully create the invoice and some lines while others fail, leaving the data in an inconsistent state.
4. ExecuteTransactionRequest vs. ExecuteMultipleRequest: A Summary
Choosing between these two messages is a critical architectural decision that frequently appears in PL-400 scenarios. The choice depends entirely on your primary goal: consistency or performance.
| Feature | ExecuteTransactionRequest |
ExecuteMultipleRequest |
|---|---|---|
| Primary Goal | Data Consistency (Atomicity) | Performance (Throughput) |
| Transaction Scope | Single database transaction for all requests | Each request runs in its own transaction |
| Failure Behavior | "All or nothing". One failure rolls back everything. | Operations are independent. ContinueOnError controls if the batch stops. |
| Typical Use Case | Multi-step operations that must complete as a single unit (e.g., financial postings, creating a header and its required lines). | Bulk data migrations, integrations where records are not dependent on each other. |

A final important constraint: you cannot nest these requests. An ExecuteTransactionRequest cannot contain an ExecuteMultipleRequest or another ExecuteTransactionRequest.
Conclusion
In this lesson, you've learned how to enforce strict data consistency across multiple Dataverse operations. By leveraging ExecuteTransactionRequest, you can group a series of actions into a single atomic transaction, guaranteeing that your data is never left in a partially completed or invalid state.
Key Takeaways:
ExecuteTransactionRequestensures that a collection of operations either all succeed or all fail together.- If any operation within the transaction fails, all previous operations in that transaction are automatically rolled back.
- This is the ideal pattern for scenarios requiring high data integrity, such as creating a header record with its required child records.
- Use the
ExecuteTransactionFaultobject to identify which specific operation caused the transaction to fail. - Choose
ExecuteTransactionRequestfor consistency andExecuteMultipleRequestfor performance.
Up Next:
We have now explored how to manage multiple operations at the service level for both performance and consistency. In the next lesson, we will shift our focus to error handling within the scope of a single operation. We will learn how to handle exceptions and implement retry logic in plugin development, giving you the tools to build more resilient and robust server-side code.