A new developer joins the platform team and ships a pre-validation plug-in that takes 800 milliseconds to run. Every save on the Account form now feels sticky. The plug-in works; the user experience does not. Plug-in development rewards discipline because every line of code runs inside a customer-facing transaction, and the cost of getting it wrong is visible immediately.
What Plug-ins Are
Server-side .NET code that runs on Dataverse events — create, update, delete, assign. Synchronous (inline with the transaction) or async (queued). Plug-ins are the right tool when the logic must run server-side, must be transactional, and cannot be expressed in a flow or a business rule.
Plug-in fits when:
- Logic must enforce data integrity at the API level
- The change must roll back if the parent operation fails
- Performance below 200ms matters
- Logic spans multiple tables in one transaction
Pre vs Post
Pre-validation runs before the transaction, with access to input and pre-image. Post-operation runs after the write, with access to the target record. Pick based on whether you need to mutate input or react to the outcome.
Pre-validation: validation that should block before any write
Pre-operation: mutation of the input before write
Post-operation: reaction after write commits
Post-async: deferred work, retry-friendly
The pre-operation stage is where most beginner mistakes land. It is the right place to set field defaults; it is the wrong place to call external APIs.
Development
Plugin Registration Tool registers assemblies to Dataverse. ALM-friendly workflow: code in Visual Studio, unit test, deploy via Azure DevOps pipelines. The pac CLI replaces the Plugin Registration Tool for scripted deployments and is the modern default.
pac plugin push --assembly bin/Release/Contoso.Plugins.dll
The push command updates the assembly and any registered steps in one call. Pair it with a build pipeline that pushes on merge to the main branch.
Testing
FakeXrmEasy and similar frameworks unit-test plug-ins without a live Dataverse. Integration tests hit a dev environment. Both layers matter. Unit tests catch logic errors fast; integration tests catch the misregistration that unit tests cannot see.
// FakeXrmEasy unit test pseudocode
const context = new XrmFakedContext();
context.Initialize(new[] { existingAccount });
const plugin = new AccountValidationPlugin();
context.ExecutePluginWith<AccountValidationPlugin>(target, "Create");
Assert.AreEqual("VALID", context.GetEntity<Account>(target.Id).status);
Cover at least the happy path and one error path per plug-in. Skipping the error path means the plug-in’s failure mode is undefined.
Performance
Plug-ins run in-transaction. Slow plug-ins slow user saves. Cap synchronous plug-in runtime; move heavy work async. The Dataverse two-second sync timeout is a hard ceiling; in practice, anything over 300 milliseconds noticeably degrades the form experience.
Performance budget for sync plug-ins:
- Read operations: under 100ms
- Validation logic: under 50ms
- External calls: never sync, always async
- Total runtime: target under 200ms, hard cap 800ms
Profile every plug-in with stopwatch logging in the early dev cycles. The cost of fixing a slow plug-in after it ships is much higher than the cost of measuring during build.
Sandbox Isolation
All plug-ins run in sandbox isolation in Dataverse online. The sandbox prevents file system access, restricts network calls, and limits memory. Code that worked on-prem in full trust often fails sandbox validation. Run the Solution Checker against every plug-in assembly before deploy.
Image Configuration
Plug-ins consume pre-images and post-images for context. The image is configured at registration time and includes only the fields you list. Listing fewer fields keeps the plug-in fast; listing more makes the logic less brittle. The sweet spot is fields the plug-in actually reads, no more.
Image configuration for AccountUpdate:
PreImage attributes: name, statecode, ownerid
PostImage attributes: name, statecode, ownerid, modifiedon
Telemetry
Wire ITracingService output to App Insights. The Dataverse plug-in trace log is convenient for occasional debugging but not for production observability. App Insights gives you correlation across plug-in, flow, and downstream systems.
What to do this week
Audit your existing plug-ins for sync runtime above 300 milliseconds and either optimize or move to async. Stand up the unit test layer with FakeXrmEasy and cover the happy path on your top three plug-ins. Wire App Insights for the next plug-in you ship.