-
Notifications
You must be signed in to change notification settings - Fork 16
howto debug plugin
Debugging a Dataverse plugin is hard when the failure only happens inside the sandbox. DynamicsCrm.DevKit helps by writing the real RemoteExecutionContext to Plugin Trace Log as a copy-pasteable JSON snippet, then replaying that context locally from a Console project.
| Part | Why it matters |
|---|---|
| Shared Project Template | Contains DevKitJson, compression helpers, and ITracingService.DebugContext(context). |
| Server Project Template | Hosts the plugin assembly and deployment scripts. |
| C# Plugin Item Template | Creates the plugin class that receives IPluginExecutionContext and ITracingService. |
| Console Project Template | Contains Helper.DebugPluginWith<T>(json, App.Service) for local replay against Dataverse. |
| Test Project Template and Shared Test Project Template | Useful when the trace scenario should become a repeatable FakeXrmEasy unit test. |
DebugContext serializes the current plugin execution context using DevKitJson. The output is written to Plugin Trace Log as C# code:
var json = @"{...}".Replace("'", "\"");If the context is larger than the Plugin Trace Log limit, DevKit writes a compressed form instead:
var json = "...".Decompress();The generated Console Project Template already contains the replay hook:
private static void DebugPlugin()
{
//var json = "";
//Helper.DebugPluginWith<???>(json, App.Service);
}In Dataverse, enable Plugin Trace Log from System Settings > Customization.
For debugging, use a setting that captures the traces you need. During active development, logging all plugin traces is usually easier. In shared environments, switch back to a safer setting after the issue is captured.
In the plugin code created from C# Plugin Item Template, get the tracing service and execution context, then call DebugContext.
public void Execute(IServiceProvider serviceProvider)
{
var tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
tracingService.DebugContext(context);
// Your plugin logic.
}DebugContext is compiled under #if DEBUG, so deploy a Debug build when you want trace replay data. Do not leave noisy debug tracing enabled in production longer than needed.
Use the server project deployment script or the CLI server command.
.\deploy.debug.bator:
devkit server --auth FromPac --pacprofile DEV --json DynamicsCrm.DevKit.Cli.json --profile DEBUGSee Server Project Template and CLI server command for the deployment profile.
Run the user action that triggers the plugin:
- create or update a row,
- qualify a lead,
- call a Custom API,
- run the business process that fails.
Then open Plugin Trace Log and find the trace entry for the failing execution. Copy the generated var json = ...; snippet from the trace message.
If you use an AI agent connected to the DevKit MCP server, you do not need to manually copy the full trace message.
Open the Plugin Trace Log row in Dataverse, copy the browser URL, and ask the AI:
Use DevKit MCP to parse this Plugin Trace Log URL, open the trace log detail, extract the DebugContext JSON snippet, and update my Console project DebugPlugin method.
<paste Plugin Trace Log URL>
The AI can use:
| MCP tool | Purpose |
|---|---|
| parse_record_url | Extracts the Plugin Trace Log record ID from the Dataverse URL. |
| get_plugin_trace_logs | Opens the trace log detail and reads the full messageblock and exception details. |
For compressed traces, ask the AI to keep the generated .Decompress() call. For normal traces, ask it to preserve the generated Replace("'", "\"") form. The goal is to update the Console project with the exact trace payload, not a summarized version.
Useful prompt:
Here is the Plugin Trace Log URL. Extract the DevKit DebugContext snippet from messageblock and replace the json assignment inside Program.DebugPlugin().
Create a project from Console Project Template if you do not already have one. Configure its connection so App.Service points to the same Dataverse environment or to an environment with equivalent data.
In Program.cs, paste the trace snippet and call Helper.DebugPluginWith<T>.
private static void DebugPlugin()
{
var json = @"{'__type':'RemoteExecutionContext'}".Replace("'", "\"");
Helper.DebugPluginWith<PreUpdateAccount>(json, App.Service);
}If the trace used compressed output, keep the generated Decompress() call:
private static void DebugPlugin()
{
var json = "base64-compressed-context".Decompress();
Helper.DebugPluginWith<PreUpdateAccount>(json, App.Service);
}Then call DebugPlugin() from Main.
static void Main()
{
DebugPlugin();
}Now set breakpoints in the plugin class and run the Console project. DevKit rebuilds the service provider, plugin execution context, tracing service, and organization service factory from the trace data so the plugin can be replayed locally.
After the bug is understood, move the scenario into a test project.
Use Test Project Template with Shared Test Project Template. The shared test helpers support:
var ctx = PluginContextBuilder.FromJson(_context, json).Build();
_context.ExecutePluginWith<PreUpdateAccount>(ctx);or:
_context.ExecutePluginFromJson<PreUpdateAccount>(json);This turns a production trace into a regression test.
| Problem | Fix |
|---|---|
| No JSON appears in Plugin Trace Log | Confirm Plugin Trace Log is enabled and the plugin assembly was deployed from a Debug build. |
| Trace says the context exceeds 10 KB | Reduce images/attributes for the plugin step, or reproduce with less data. DevKit already tries compact JSON and compression. |
| Console replay hits Dataverse data differences | Point the Console project to the same environment or create the required data before replay. |
| Breakpoints do not hit | Make sure the Console project references the current plugin project output and you are debugging the Console startup project. |