The Plug-in Pipeline: How Dataverse Processes Events
Understand the engine behind every Dataverse operation. Learn the event execution pipeline stages, the execution context, and how Pre/Post Images give your plug-in a before-and-after view of data.
The pipeline that powers Dataverse
Think of the Dataverse pipeline like an airport security checkpoint.
When you fly, you go through multiple stages: ticket check (are you even allowed here?), security screening (pre-check before you enter the secure area), and boarding (the main action). After boarding, there is a post-departure check (update the passenger manifest).
Every Dataverse operation (create a record, update a field, delete a row) passes through a similar pipeline with four stages. Your plug-in code can intercept the operation at any stage β to validate data before it is saved, modify it during processing, or trigger actions after the save completes.
Understanding these stages is the single most important concept for PL-400 Domain 5.
The four pipeline stages
| Stage | When It Runs | Transaction? | Can Cancel Operation? | Best For |
|---|---|---|---|---|
| Pre-validation (Stage 10) | Before security checks | No β outside transaction | Yes | Light validation, early rejection |
| Pre-operation (Stage 20) | After security, before DB write | Yes β inside transaction | Yes | Modify data before save, complex validation |
| Main operation (Stage 30) | The actual database operation | Yes β the core transaction | N/A (system only) | Cannot register plug-ins here |
| Post-operation (Stage 40) | After the DB write | Yes β still inside transaction | Yes (rolls back entire transaction) | Create related records, trigger integrations |
Synchronous vs asynchronous execution
| Mode | Runs When | User Experience | Use For |
|---|---|---|---|
| Synchronous | Immediately, blocks the operation | User waits | Validation, data modification, must-run logic |
| Asynchronous | After the transaction commits, in background | User continues working | Notifications, logging, non-critical processing |
Exam tip: The stage selection pattern
The exam will describe a scenario and ask which stage is correct. Use this decision tree:
- Need to reject invalid data before security checks run? β Pre-validation
- Need to modify or enrich data before it is saved? β Pre-operation
- Need to create related records or trigger integrations after save? β Post-operation (sync)
- Need to send notifications or log events without blocking the user? β Post-operation (async)
Red flag: If an answer says βregister on the Main operation stageβ β it is always wrong. You cannot register plug-ins on Stage 30.
The execution context
Every plug-in receives an IPluginExecutionContext that tells you everything about the current operation:
public class ShipmentValidator : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Get the execution context
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
// What operation triggered this plug-in?
string messageName = context.MessageName; // "Create", "Update", "Delete"
// What entity is being affected?
string entityName = context.PrimaryEntityName; // "shipment"
// What stage are we in?
int stage = context.Stage; // 10, 20, 30, or 40
// Who triggered the operation?
Guid userId = context.UserId; // The calling user
Guid initiatingUserId = context.InitiatingUserId; // Original user (even through impersonation)
// What data is being sent? (for Create/Update)
Entity target = (Entity)context.InputParameters["Target"];
string destination = target.GetAttributeValue<string>("destination");
// What depth are we at? (prevents infinite loops)
int depth = context.Depth; // 1 = original, 2+ = triggered by another plug-in
}
}
Key context properties
| Property | What It Tells You |
|---|---|
| MessageName | The operation: Create, Update, Delete, Retrieve, etc. |
| PrimaryEntityName | The table being affected |
| Stage | Which pipeline stage (10, 20, 30, 40) |
| UserId | The user executing the operation (may be impersonated) |
| InitiatingUserId | The original user who started the chain |
| InputParameters[βTargetβ] | The record being created/updated (Entity object) |
| Depth | Recursion depth (check to prevent infinite loops) |
| PreEntityImages | Snapshot of the record BEFORE the operation |
| PostEntityImages | Snapshot of the record AFTER the operation |
Pre Images and Post Images
Images are snapshots of a record at specific points in the pipeline.
| Feature | Pre Image | Post Image |
|---|---|---|
| Shows | Record values BEFORE the operation | Record values AFTER the operation |
| Available in | Pre-validation, Pre-operation, Post-operation | Post-operation only |
| Useful for | Comparing old vs new values, checking what changed | Verifying final state, accessing calculated fields |
| Contains | Fields you registered for in the image definition | Fields you registered for in the image definition |
| On Create | Not available (no previous state) | Available in post-operation |
| On Delete | Available (record exists before delete) | Not available (record is gone) |
Using images to detect changes
// In a Post-operation Update plug-in
Entity preImage = context.PreEntityImages["preImage"];
Entity postImage = context.PostEntityImages["postImage"];
// statuscode is an OptionSetValue, not a string
int oldStatus = preImage.GetAttributeValue<OptionSetValue>("statuscode")?.Value ?? -1;
int newStatus = postImage.GetAttributeValue<OptionSetValue>("statuscode")?.Value ?? -1;
if (oldStatus != newStatus)
{
// Status changed β trigger notification
tracingService.Trace($"Status changed from {oldStatus} to {newStatus}");
}
Scenario: Kai uses images to track shipment changes
Kai registers a Post-operation Update plug-in on the Shipment table at LogiFlow. He configures:
- Pre Image: includes
status,destination,priorityfields - Post Image: includes
status,destination,priorityfields
When a shipmentβs status changes from βIn Transitβ to βDeliveredβ, the plug-in compares the pre and post images, sees the status change, and creates a Delivery Confirmation record with the delivery timestamp.
Why images instead of Target? The Target entity in an Update only contains the fields that were actually changed. If Kai needs to check the destination (which was not changed in this update), it would not be in Target β but it IS in the Pre Image.
A developer needs to validate that a Shipment record's weight does not exceed 1000kg BEFORE security checks run. If the weight exceeds the limit, the operation should be cancelled immediately. Which pipeline stage should the plug-in be registered on?
A plug-in registered on Post-operation Update needs to check whether the Priority field changed. The developer reads context.InputParameters['Target'] but Priority is not in the Target entity. Why?
π¬ Video coming soon
Next up: Writing Plug-ins β implementing business logic with the Organisation Service, performance optimisation, and plug-in registration.