Dataverse plug-ins explained

How Dataverse plug-ins work — pipeline stages, sync vs async, registration, debugging, and when to use a plug-in vs a Power Automate flow.

Updated 2026-05-04

A Dataverse plug-in is a piece of .NET code that runs server-side in response to Dataverse events. Plug-ins are the most powerful customisation mechanism in the CRM-side Dynamics 365 stack — they run in the same transaction as the originating operation, can read and modify the entire context, and can call any external service. They are also the most demanding mechanism, with operational realities that Power Automate flows usually let you avoid.

The event pipeline. Every Dataverse operation (Create, Update, Delete, Associate, etc.) runs through a defined pipeline with four stages where plug-ins can register:

  1. Pre-validation (stage 10) — runs outside the database transaction, before any validation. Used for plug-ins that need to inspect or modify the request before security or duplicate-detection runs. Common for caller-context-based logic.

  2. Pre-operation (stage 20) — runs inside the transaction, before the database write. Used to modify the input target (e.g. compute a derived field that should be stored), or to throw an exception to cancel the operation.

  3. Main operation — the database write itself. No custom plug-ins here.

  4. Post-operation (stage 40) — runs inside the transaction, after the database write. Used to do follow-on work that should be transactional with the main operation: write a related record, raise a notification, call an external API (carefully).

Synchronous vs asynchronous. Plug-ins register as either:

  • Synchronous — runs immediately, blocks the user's request until complete. Failures roll back the transaction. Use for operations that must complete before the user moves on, or that need transactional rollback semantics.

  • Asynchronous — queued for later execution by the async service. Failures are retried; the user doesn't wait. Use for follow-on work that doesn't need to complete synchronously: notifications, integrations, indexing.

The execution context. Each plug-in receives an IPluginExecutionContext with the originating message, the target entity, the user, the depth (to prevent infinite recursion if plug-ins trigger each other), and shared variables for inter-plug-in data passing.

Registration. Plug-ins are packaged as signed .NET assemblies and registered through the Plug-in Registration Tool or via solution import. Registration includes: assembly, plug-in class, message (Create/Update/etc.), entity, stage, filtering attributes (only fire when these fields change), and async flag.

When to use plug-ins vs Power Automate flows.

  • Use plug-ins when:
    • The logic must be in the same transaction as the originating operation.
    • Performance demands milliseconds rather than seconds.
    • You need access to the pre/post images of the entity.
    • You need to throw exceptions to cancel the operation cleanly.
  • Use Power Automate flows when:
    • The logic is asynchronous and orchestration-heavy.
    • It crosses systems through connectors.
    • It involves human approval.
    • Maintenance is by low-code makers, not developers.

In modern practice, flows replace many plug-ins; plug-ins survive for transactional, performance-sensitive, server-only logic.

Debugging. Plug-ins are notoriously hard to debug. The Plug-in Registration Tool supports plug-in profiling that captures runtime context and lets you replay it locally in Visual Studio. Trace logs are visible in the Plug-in Trace Log if enabled.

Operational caveats. Synchronous plug-ins block the user; slow plug-ins make the system feel slow. Asynchronous plug-ins compete for the async service queue; bursty volume can backlog. External API calls from inside plug-ins risk timeouts; wrap them or move to async.

Related guides