Skip to content

Plugins

Peter McDonald edited this page Aug 12, 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

New projects scaffold without a sample class — the wizard offers to create your first plugin class at the end of project creation. After that, right-click the project folder and choose Create Plugin Class or Create Workflow Class (also in the project card's menu), then enter a name. The .csproj includes all files in the folder, so no project edits are needed.

Build Package & Deploy needs at least one plugin class — an assembly with no plugin types is rejected by Dataverse, so the extension checks first and tells you.

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.

Generation runs pac modelbuilder and authenticates automatically from your project's connection (service-principal connections create/refresh the extension's own dataverse-powertools pac profile; interactive connections use your active pac auth profile).

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: two options, both covered in Debugging Plugins — read server-side traces with View Plugin Trace Logs, or Profile next run (one-click on Windows; capture in the Plugin Registration Tool on other OSes) and replay it as a unit test you F5-debug in VS Code with the exact captured execution context — no live org needed.


Viewing plugin trace logs

View Plugin Trace Logs (plugin card ⋯, or the Command Palette) pulls the latest plugintracelog records from the environment and opens the selected one as a formatted document — message, entity, mode, duration, exception details and your ITracingService.Trace(...) output. Enable plug-in trace logging in the org's System Settings for records to appear.

Traces tell you what happened; to step through the code that produced them, replay a captured profile as a debuggable unit test.

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. Support for that path was removed in 1.0.3. Such a project still opens and still tells you how to move, but its commands now run the modern pac/dotnet flow above, which expects the v3 layout — so migrate rather than rely on it. Upgrading Projects has the steps.


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