Blog 9: Resolving You Can’t Start a Transaction with a Different Isolation Level

Dataverse plug-ins run within a managed transaction, ensuring data consistency and rollback capabilities. However, attempting to manually create a transaction inside a plug-in can cause the following error:

🚨 Error Message:

Error Code: -2147220989  
Error Message: You cannot start a transaction with a different isolation level than is already set on the current transaction.

This error occurs when your plug-in tries to initiate a new database transaction while already operating within an existing one.

In this blog, we’ll explore why this happens and how to properly handle database operations within plug-ins.


🔍 Why Does This Error Occur?

This error typically occurs when:

1️⃣ Explicitly Creating a New Transaction Inside a Plug-in

  • Using TransactionScope or a direct SQL BEGIN TRANSACTION in a plug-in is unnecessary and conflicts with Dataverse’s internal transaction.

2️⃣ Calling Another Plug-in That Tries to Start a Transaction

  • If a parent plug-in calls another plug-in that also attempts to manage a transaction, it results in a conflict.

3️⃣ Using External Database Transactions Within Dataverse Plug-ins

  • Direct SQL operations (e.g., via an Azure Function or External API) that attempt to start their own transactions may cause this issue.

4️⃣ Performing Metadata Changes in a Synchronous Plug-in

  • Schema modifications (e.g., adding columns or updating relationships) are not supported in synchronous plug-ins because they inherently require transactions.

🔧 How to Fix This Error

1️⃣ Avoid Creating New Transactions in Plug-ins

Dataverse already manages transactions for you, so avoid using TransactionScope in your plug-in code.

Incorrect Code (Causes Error)

using (TransactionScope scope = new TransactionScope()) 
{
    service.Create(new Entity("account") { ["name"] = "New Account" });
    scope.Complete();
}

This conflicts with the existing Dataverse transaction and leads to the error.

Correct Approach (Let Dataverse Handle Transactions)

service.Create(new Entity("account") { ["name"] = "New Account" });

Simply use IOrganizationService for database operations—Dataverse ensures transaction integrity automatically.


2️⃣ Use Asynchronous Plug-ins for Long-Running Transactions

If your operation involves multiple steps or external data sources, consider asynchronous plug-ins to avoid transaction conflicts.

📌 Steps to Convert a Plug-in to Async:

  • In Plug-in Registration Tool, set the Execution Mode to Asynchronous instead of Synchronous.
  • Remove any explicit transaction handling in your code.

3️⃣ Avoid Schema Modifications in Plug-ins

Metadata changes (e.g., adding tables, updating fields) should not be done inside synchronous plug-ins. Instead:

✔ Use Power Automate or Azure Functions for metadata operations.
✔ If necessary, perform metadata changes asynchronously.


4️⃣ Handle External API Calls Without Transactions

If your plug-in interacts with an external API that performs database updates, ensure the API does not start a new transaction.

Best Practice: Use stateless APIs that accept data without managing transactions.


📌 Best Practices to Avoid This Error

Never use TransactionScope or BEGIN TRANSACTION inside a plug-in.
Use asynchronous execution for complex operations.
Avoid schema modifications inside plug-ins.
Ensure external API calls don’t start new transactions.


📢 Coming Up Next…

In Blog 10, we’ll discuss a known issue with Activity.RegardingObjectId, explaining why the Regarding field sometimes appears blank and how to ensure correct lookup behavior in plug-ins.

Stay tuned for another troubleshooting deep dive! 🚀

Leave a comment