Dataverse plug-in

What is a Dataverse plug-in?

A Dataverse plug-in is custom server-side code that runs when Microsoft Dataverse processes a registered data operation. You compile the code into a .NET assembly and connect it to the platform's event framework through a step.

A step spells out which message the plug-in reacts to, which table, which stage of the processing pipeline, and whether it runs synchronously or asynchronously. Messages cover the everyday operations, Create, Update, and Delete, as well as more specialised platform operations.

Plug-ins are for logic that the no-code options cannot express well: validation that has to happen inside the database transaction, complex derivation of values, or a server rule that must apply the same way to every client. Microsoft's own advice is to try the declarative tools first and reach for a plug-in only when they fall short.

IPlugin and the execution context

A plug-in class implements the IPlugin interface and its single Execute method. Dataverse hands your code a service provider, from which you pull the execution context, tracing, and the organization service you use to read and write data.

public void Execute(IServiceProvider services) {
  var context = (IPluginExecutionContext)services.GetService(typeof(IPluginExecutionContext));
  if (context.MessageName != "Update") return;
}

The context carries the message, stage, depth, user, a correlation ID, the input parameters, and shared variables. One detail trips people up: on an update, the Target parameter usually holds only the columns that changed, not the whole record. If you need other values, ask for them deliberately.

The pipeline stages

Each message runs through a series of stages, and where you register a step decides what it can do.

PreValidation runs early and often outside the main transaction. It is the place to stop an operation with a clear error before the platform does any heavy work, and it happens before security checks.

PreOperation runs just before the core operation, inside the transaction. This is where a plug-in adjusts values on the Target without issuing a separate update.

PostOperation runs after the core operation. A synchronous failure here rolls the whole operation back. You can also register a PostOperation step to run asynchronously, in which case it fires after the original transaction has finished, through the asynchronous service.

Pre and post images

An image is a registered snapshot of selected columns, taken before or after the operation. It saves you an extra retrieve when you need the old or final values of a record, and it performs far better than fetching the row yourself just to compare.

Register only the columns you actually use. Large images cost processing time and blur what data the plug-in really depends on. Not every message supports every image, and the Target and the images each mean something specific, so do not treat them as interchangeable copies of a full record.

A worked example: locking a case number

A case table has an external number that must not change once the case is finalised. A PreValidation step on Update compares the Target against a pre-image of the record.

If a forbidden change is detected, the plug-in throws an InvalidPluginExecutionException with a message the user can act on, and the operation is stopped before any related logic runs unnecessarily. The step uses filtering attributes so it only fires when the relevant field is part of the update, rather than on every change to the table.

Plug-in versus business rule and flow

A Dataverse business rule fits simple conditions, default values, required fields, and form behaviour. A cloud flow fits orchestration, connectors, and work that is allowed to happen after an event rather than during it.

A plug-in fits server logic that has to apply to every Dataverse client, run inside the transaction, or use more programmatic control than the other two allow. Do not use a plug-in for slow external processing inside a synchronous user action, though. When the integration can be decoupled, choose asynchronous execution, a webhook, a message queue, or a flow instead.

What to watch out for with Dataverse plug-ins

Recursion is easy to trigger. A plug-in that updates the same table can re-fire itself or other steps. Understand depth and the causal chain, but do not paper over a bad design with a depth check.

Keep implementations stateless. Members of an IPlugin class are shared and prone to thread-safety problems. Avoid parallel threads, keep queries and updates lean, and register filtering attributes and a specific table instead of broad steps.

External calls are a liability inside a transaction. A slow or unavailable service stretches the user action and the transaction with it. Design retries and idempotence outside the transaction when that fits the work better.

Treat registration as code. Include the assembly and step registrations in a solution, use tracing for diagnostics, return errors without sensitive detail, and validate the registered configuration on every deployment.

Last Updated: July 18, 2026 Back to Dictionary
Keywords
Dataverse plug-in Microsoft Dataverse IPlugin execution pipeline PreValidation PreOperation PostOperation Cloud flow webhook Idempotence C#