-
Notifications
You must be signed in to change notification settings - Fork 2
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.)

Steps: Prerequisites → Create a class → Register steps → Early-bound types → Build → Deploy → Unit 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.
- The .NET SDK and the Power Platform CLI (
pac) — see Getting Started. - A connected environment (early-bound generation and deploy need it).
- The C# extension for editing/IntelliSense.
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.
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.
Use Generate Early Bound (or the Early Bound Options view) to generate
strongly-typed classes for your tables and messages, powered by pac modelbuilder.
- Open Configure Plugin Early Bound Settings to set the namespace, output directory, service-context name, and other model-builder options.
- 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).
- 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.
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.
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 any OS since 1.0.7) and replay it as a unit test you F5-debug in VS Code with the exact captured execution context — no live org needed. Running that test needs .NET Framework (Windows, or mono).
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.
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 testand 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.
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.
| 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. |
- Debugging Plugins — capture a profile in PRT, replay it as a debuggable unit test
- Plug-in development overview
- Best practices for plug-ins
- Debug plug-ins
- Register a plug-in