Skip to content

Plugins

pete-mc edited this page Jul 9, 2026 · 16 revisions

Plugins

The Plugins project type covers the full plugin and custom-workflow-activity lifecycle: create classes, register steps, generate early-bound types, build, deploy as a plugin package, and unit test.

New plugin projects are created with pac plugin init, so they're SDK-style and build with dotnet on any OS. (Plugin assemblies target .NET Framework 4.6.2 — a Dataverse sandbox requirement — but dotnet build compiles that on Windows, macOS, and Linux.)

Plugin menu

Steps: PrerequisitesCreate a classRegister stepsEarly-bound typesBuildDeployUnit test.

Before you start: skim Best practices for plug-in development — stateless classes, minimal footprint, correct stage/mode choices — it will save you a lot of debugging later.


Prerequisites


Step 1 — Create a plugin / workflow class

Right-click the project folder and choose Create Plugin Class or Create Workflow Class, then enter a name. The .csproj includes all files in the folder, so no project edits are needed.

public class ClassName : IPlugin
{
    public void Execute(IServiceProvider serviceProvider)
    {
        var tracer = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
        var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
        var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
        var service = factory.CreateOrganizationService(context.UserId);
        // ...
    }
}

Best practice: keep plug-in classes stateless — don't store per-request data in fields, since the platform reuses instances across invocations. See Develop IPlugin implementations.


Step 2 — Register steps (CodeLens)

Steps are registered from attributes on your class. Add or edit them with the CodeLens actions above each class in a C# file, or the Add Plugin Decoration / Add Workflow Decoration commands. A unique Id GUID is generated for you.

[CrmPluginRegistration(MessageNameEnum.Create, "contact", StageEnum.PostOperation,
  ExecutionModeEnum.Synchronous, "", "Create Contact step", 1, IsolationModeEnum.Sandbox,
  Id = "90705ddd-1442-4403-8cb6-48807e2ecaf7")]
public class ClassName : IPlugin { /* ... */ }

You'll be prompted for the message, table, stage, execution mode, filtering attributes, step name, order, and isolation mode. Use Update Filtering Attributes on the CodeLens to change which columns trigger the step.

Best practice: register on the narrowest message/table/stage that does the job, set filtering attributes so the step only fires when relevant columns change, and prefer Sandbox isolation. See Event framework.


Step 3 — Generate early-bound types

Use Generate Early Bound (or the Early Bound Options view) to generate strongly-typed classes for your tables and messages, powered by pac modelbuilder.

  1. Open Configure Plugin Early Bound Settings to set the namespace, output directory, service-context name, and other model-builder options.
  2. In the Early Bound Options view, pick the tables and messages to generate (refresh the list from Dataverse and add/remove with the inline controls).
  3. Click Generate Early Bound.

Verify: the output directory fills with generated .cs files.

More on early-bound: Generate early-bound classes and pac modelbuilder.


Step 4 — Build

Run Dataverse PowerTools: Build Locally to dotnet build the plugin project.

Verify: the build succeeds and a NuGet package (.nupkg) is produced under bin/Release.


Step 5 — Deploy

Run Dataverse PowerTools: Build Package & Deploy to build and deploy the plugin as a plugin package to your environment. Plugin packages let dependent assemblies ship together and make versioning cleaner than single-assembly deployment. The extension also registers your CodeLens-declared steps and workflow activities.

Verify: the log shows the package created/updated in Dataverse; the steps appear against the plugin in the maker portal.

Debugging: to debug a deployed plug-in, use the Plug-in Registration Tool profiler / Plugin Trace Log. Add ITracingService.Trace(...) calls and read them in the Plugin Trace Log.


Step 6 — Unit testing

Run Setup Unit Testing to add a test project wired up with DataverseUnitTest. Choose your framework (MSTest, xUnit, or NUnit); the extension creates the project, references your plugin, installs the package, targets a compatible framework, and adds a boilerplate test.

  • Create Plugin Test — scaffolds a new test class.
  • Run Tests — runs dotnet test and reports results in the output channel.
public class PluginTests
{
    [Fact]
    public void Example()
    {
        // Arrange a DataverseUnitTest context, execute the plugin, assert the result.
    }
}

Best practice: unit-test business logic against a mocked organization service so tests run fast and offline, and reserve environment deploys for integration checks.


Legacy projects

Plugin projects created before template version 3 used spkl and are deprecated. They still load, but new features target the modern pac/dotnet flow above — create a new plugin project to migrate.


Troubleshooting

Symptom Fix
Early-bound generation fails to connect Re-connect — see Refreshing a stale connection.
Deploy fails validating a strong name Plugin assemblies must be strong-named for the assembly API; the modern package flow handles this — rebuild with Build Package & Deploy.
Steps don't fire after deploy Check the CodeLens registration (message/table/stage/filtering attributes) and that the step is enabled in the maker portal.
A plug-in throws at runtime Add ITracingService.Trace(...) and read the Plugin Trace Log.

Learn more

Clone this wiki locally