Pre Images and Post Images
Complete the full lesson to earn 25 points
Work through each section, then tap “Mark as Complete” on the last one.
✦ Skip the page breaks and see fewer ads — read each lesson on a single page with Pro
Mastering Dataverse Plug-ins: The Power of Pre-Images and Post-Images
Introduction to Dataverse Extensibility
When you work with Microsoft Dataverse, you quickly realize that standard configuration—like business rules, workflows, or Power Automate flows—can only take you so far. There comes a point in almost every complex business application where you need to execute custom logic that is both performant and reliable. This is where Dataverse plug-ins come into play. A plug-in is essentially custom compiled code, written in C#, that runs on the server side in response to events triggered within the Dataverse environment.
While writing the logic itself is the core of the task, understanding how to access the data involved in those events is what separates a novice developer from a professional. This is where "Images" enter the picture. In the context of Dataverse plug-ins, images are snapshots of the data as it existed before or after a database operation. Without these images, your code would be forced to perform additional queries to the database, which leads to performance bottlenecks and unnecessary complexity. Understanding how to register, retrieve, and use these images is critical for building efficient and scalable extensions.
Why Images Matter: The Performance Perspective
To understand why we need images, we must look at how the Dataverse event execution pipeline works. When a user updates a record, that update process goes through several stages: the "Pre-Operation" stage, the "Main Operation" (where the database is actually updated), and the "Post-Operation" stage. If you are writing a plug-in that runs in the Post-Operation stage, you might need to know what the value of a field was before the update occurred to calculate a delta or audit a change.
If you do not use a Pre-Image, your code would have to execute an IOrganizationService.Retrieve call to fetch the record from the database. This adds a network hop and a database query to your transaction. In a high-volume system, hundreds of these extra queries can lead to significant latency and potential locking issues. Images solve this by providing the data to your plug-in via the IPluginExecutionContext at the moment the execution starts, meaning the data is already in memory and ready for use without any extra round-trips to the database.
Callout: The "Snapshot" Analogy Think of a Dataverse plug-in as a security camera. The "Target" entity in your plug-in context tells you what changed, but it doesn't always tell you the full story. A Pre-Image is like a photo taken just before the event happened, and a Post-Image is a photo taken immediately after the event finished. By having these photos, you don't need to go back and check the security logs (the database) to see what was there before or after the event; the evidence is already sitting in your hand.
Understanding Pre-Images
A Pre-Image is a snapshot of the entity's attributes as they existed in the database before the event processing pipeline modified them. You register Pre-Images for specific stages, most commonly the Pre-Operation stage. Because the data is fetched by the platform just before your code runs, you are guaranteed that the values represent the state of the record immediately prior to the event.
When to Use a Pre-Image
You should reach for a Pre-Image whenever your business logic requires a comparison between the "old" value and the "new" value. Common scenarios include:
- Change Tracking: Determining if a specific field (like an "Account Status") has changed from "Active" to "Inactive" so you can trigger a specific notification or secondary process.
- Calculation Validation: Ensuring that a new value being entered is within a specific range relative to the existing value.
- Audit Logging: Capturing the previous state of a record to store in a custom history entity before it gets overwritten.
Registering a Pre-Image
You do not define images inside your C# code. Instead, you define them in the Plug-in Registration Tool (PRT) or via an automated deployment pipeline. When you register a step (e.g., Update of Account), you will see an "Images" section. You must provide a name for the image—which acts as the key to look it up in your code—and specify which attributes you want to include. It is best practice to select only the fields you actually need, as this reduces the memory footprint of your plug-in.
Understanding Post-Images
A Post-Image is a snapshot of the entity's attributes after the main database operation has completed. This is most useful in the Post-Operation stage. While the Target entity in the context contains the values that were sent to be updated, it only contains the fields that were actually changed. If you need to access fields that were not part of the update request but were updated by the system (such as auto-generated numbers or calculated fields), the Post-Image is your only reliable source.
When to Use a Post-Image
- Retrieving System-Generated Values: If a plug-in or workflow updates a field like a "Reference Number" during the transaction, the
Targetobject won't have it. A Post-Image will. - Post-Processing Logic: If your logic needs to perform an action based on the final state of the record, such as sending an email with the final calculated total of an order.
- Integration Triggers: When you need to send an object to an external system, you usually need the full state of the record after all business rules have been applied.
Note: Do not attempt to use a Post-Image in the Pre-Operation stage of the pipeline. The database transaction has not yet finalized, so the "post" state does not exist yet. Attempting to access an image that hasn't been populated will result in a
KeyNotFoundExceptionat runtime.
Practical Implementation: A Step-by-Step Guide
Let’s walk through a concrete example. Imagine we have a requirement: whenever a "Project" entity's EstimatedBudget field is updated, we must check if the budget has increased by more than 20%. If it has, we need to flag the project for management review.
Step 1: Registration
- Open the Plug-in Registration Tool and connect to your Dataverse environment.
- Register your assembly and create a new Step on the
Updatemessage for theProjectentity. - Set the stage to
Pre-Operation. - In the "Registered Images" area, click "Register New Image."
- Give it a name, such as
PreProjectUpdate. - Select the attributes you need:
estimatedbudget. - Save the step.
Step 2: The Code Implementation
Now, you write the C# code to access this image within your Execute method.
public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
// Check if the image exists in the context
if (context.PreEntityImages.Contains("PreProjectUpdate") &&
context.PreEntityImages["PreProjectUpdate"] is Entity preImage)
{
// Access the target (the incoming changes)
Entity target = (Entity)context.InputParameters["Target"];
// Ensure the field we are checking is actually in the target
if (target.Contains("estimatedbudget"))
{
decimal oldBudget = preImage.GetAttributeValue<Money>("estimatedbudget").Value;
decimal newBudget = target.GetAttributeValue<Money>("estimatedbudget").Value;
if (newBudget > (oldBudget * 1.20m))
{
// Logic to flag the project for review
target["reviewrequired"] = true;
}
}
}
}
Explanation of the Code
- Accessing the Context: We cast the service provider to
IPluginExecutionContext. This is the gateway to all event data. - Key Check: We use
.Contains("PreProjectUpdate")to ensure the image we registered is actually available. This prevents null reference exceptions if someone accidentally deletes the image registration. - Casting: We cast the object from the dictionary to an
Entitytype. - Comparison: We perform the business logic by comparing the values found in the
preImage(the old state) with the values found in thetarget(the requested new state).
Comparing Pre-Images and Post-Images
To help you decide which to use, refer to the following table:
| Feature | Pre-Image | Post-Image |
|---|---|---|
| Available Stage | Pre-Operation | Post-Operation |
| Data State | Before database commit | After database commit |
| Primary Use | Validations, delta calculations | Integration, post-event actions |
| Performance | High (avoids Retrieve) | High (avoids Retrieve) |
| Source | Database snapshot | Database snapshot |
Warning: Be cautious about the number of attributes you select when registering an image. While it is tempting to select "All Attributes," this consumes memory and can affect the performance of your plug-in if the entity has many fields or large text fields. Only select the fields your logic requires.
Best Practices for Plug-in Images
1. Always Verify the Existence of the Image
Never assume that an image is present. Even if you registered it, a future developer might modify the registration or delete it. Always check context.PreEntityImages.Contains("KeyName") before accessing the dictionary. This simple check prevents your plug-ins from failing abruptly during execution, which would cause a bad user experience.
2. Use Meaningful Names
When registering images, use descriptive names such as PreUpdateAccount or PostCreateContact. This makes it much easier for other developers (or your future self) to understand what the image represents when looking at the code. Avoid generic names like Image1 or Data.
3. Attribute Selection Strategy
The Plug-in Registration Tool allows you to select specific attributes. Use this feature to your advantage. If you only need to compare the statuscode field, do not select the entire entity. Reducing the field count reduces the overhead on the Dataverse platform, which helps maintain the overall performance of your organization's instance.
4. Handling Nulls
Dataverse often returns null for fields that are empty. When using GetAttributeValue<T>, ensure your code is prepared to handle null values. For instance, if you are performing a calculation, check if the value is null before attempting to perform arithmetic on it, or you will trigger an error.
5. The "Target" is not the "Image"
A common mistake is confusing the Target parameter with the Image. The Target is a partial entity—it only contains the fields that were changed. The Image is a full entity record (or at least the fields you selected). If you need to perform a check against a field that wasn't modified in the current request, you must use an image.
Common Pitfalls and How to Avoid Them
Pitfall 1: The "Missing Data" Exception
Developers often forget to register the image in the PRT but try to access it in code. This results in a runtime error.
- How to avoid: Implement a robust "Registration Checklist." If your code depends on an image, document that requirement in the code comments or a README file. If you are using automated deployment (like the Power Platform CLI), define the image registration in your deployment configuration files so it is created automatically.
Pitfall 2: Over-fetching Data
Fetching too many attributes in an image can lead to memory pressure.
- How to avoid: Review your plug-in logic periodically. If you find you are only using 2 out of 50 fields from an image, go back to the registration and remove the unnecessary fields.
Pitfall 3: Modifying the Image
Some developers try to change the values inside the PreEntityImages dictionary to influence the outcome of the operation.
- How to avoid: Remember that images are read-only snapshots. If you need to change data before it hits the database, you should modify the
Targetobject in thePre-Operationstage. Trying to modify an image will not result in the data being saved to the database.
Callout: Why not just use Retrieve? You might wonder why you shouldn't just call
service.Retrieve()every time you need data. The answer is twofold: performance and transactional integrity. ARetrievecall is an extra database query. If you have a high-concurrency environment, those extra queries add up to significant latency. Furthermore, theRetrievecall happens after your code starts, whereas the Image is provided to your code at the exact moment of execution, ensuring you are working with the most accurate state of the system at the start of the transaction.
Advanced Scenarios: When Images aren't Enough
While Pre and Post images cover 95% of use cases, there are rare scenarios where they might not be sufficient. For example, if you need to fetch related data (like the Primary Contact of an Account), an image of the Account entity won't contain the fields of the Contact entity. In this case, you would still need to perform an IOrganizationService.Retrieve or a QueryExpression to get that related data.
However, even in these cases, you should use images for the primary entity. This minimizes your database queries to only the ones that are strictly necessary, keeping your code as lean as possible.
Troubleshooting Tips
If your plug-in is behaving unexpectedly, follow these steps:
- Trace Logs: Use the
ITracingServiceto write the contents of your images to the plug-in trace logs. This is the most effective way to see exactly what data the platform is providing to your code.tracingService.Trace("Pre-Image Value: {0}", preImage.GetAttributeValue<string>("name")); - Verify Registration: Ensure the step is registered for the correct message (Create, Update, Delete) and the correct stage (Pre-Operation, Post-Operation).
- Check Pipeline Depth: If you are dealing with recursive calls (e.g., a plug-in that updates a record, which triggers the same plug-in again), check the
context.Depth. This can help you avoid infinite loops and identify if you are in the expected execution cycle.
Summary: A Checklist for Success
When you are building your next Dataverse plug-in, keep this checklist in mind to ensure you are utilizing images effectively:
- Identify the Need: Do I need the state of the record before or after the change?
- Select the Stage: Pre-Operation for Pre-Images; Post-Operation for Post-Images.
- Register with Precision: Use the Plug-in Registration Tool to select only the required fields.
- Defensive Coding: Always check
context.PreEntityImages.Contains("Key")before accessing data. - Trace Your Work: Use
ITracingServiceto log image data during testing to verify your assumptions. - Document: Note the registration requirements in your source control or documentation so the plug-in can be deployed successfully to other environments.
Comprehensive Key Takeaways
- Images are Performance Optimizers: By providing a snapshot of the entity at the time of execution, images eliminate the need for redundant
Retrievecalls, reducing latency and database load. - Context is Key: Always use the
IPluginExecutionContextto access your images. They are not local variables; they are provided by the Dataverse platform. - Precision Matters: Only include the attributes you need in your image registration. This keeps your memory usage low and your plug-in performance high.
- Read-Only Nature: Remember that images are snapshots. They are read-only, and modifying them will not change the data in the database. Use the
Targetparameter to modify data during the Pre-Operation stage. - Registration is External: Images are configured outside of the code. Ensure your deployment process includes the registration of these images, or your code will fail at runtime.
- Defensive Programming: Always include null checks and existence checks (using
.Contains()) before accessing images to ensure your code is resilient against configuration changes. - The Right Tool for the Job: Use Pre-Images for comparison and validation, and Post-Images for post-processing and integration tasks. Knowing which to use simplifies your logic and clarifies your intent.
Frequently Asked Questions (FAQ)
Can I have both a Pre-Image and a Post-Image for the same step?
Yes, absolutely. You can register multiple images for a single step. For example, you might need a Pre-Image to compare the status field and a Post-Image to retrieve the system-generated-id after the record is created.
What happens if the field I included in the image is null?
If the field is null in the database, it will be null in your image. Your code should handle this gracefully using standard C# null checks, such as if (entity.Contains("field") && entity["field"] != null).
Is it possible to use images on a "Create" message?
You cannot have a Pre-Image on a "Create" message because the record does not exist in the database before the operation. You can, however, use a Post-Image on a "Create" message to get the values of fields that were populated by the system (like an auto-numbering field or a created-on date) immediately after the creation.
Does the user's security role affect the data in the image?
No. Plug-ins run in the context of the user who triggered the event, but the platform retrieves the image data using the system's internal service account. The image will contain all the data you registered, regardless of what the triggering user has permission to see. This is an important security consideration: if you are exposing data from an image to the user, ensure your code checks the user's permissions first.
How do I debug issues with images?
The best way is to use the Plugin Trace Log. Enable logging in your environment, trigger the event, and then open the "Plugin Trace Log" area in the Power Platform Admin Center or via Advanced Find. Look for your custom trace messages to see exactly what values were present in your images at the time of execution.
By mastering the use of Pre and Post images, you are significantly enhancing the efficiency and reliability of your Dataverse extensions. This knowledge allows you to move beyond simple CRUD operations and build sophisticated, performant business logic that scales with your organization's needs. Remember that the goal is always to balance functional requirements with system performance, and images are one of the most powerful tools in your kit for achieving that balance.
Continue the course
Enjoying the courses?
Everything stays free. Pro shows fewer ads, doubles your daily points limit so you progress twice as fast, and lets you read each lesson on one page.
- ✓ Fewer advertisements
- ✓ 2× daily points limit
- ✓ Distraction-free lessons