DEV Community

Cover image for Pillaro and XrmFramework: Two Different Approaches to Dataverse Plugin Development
Ján
Ján

Posted on

Pillaro and XrmFramework: Two Different Approaches to Dataverse Plugin Development

XrmFramework and Pillaro both try to reduce friction in Dataverse plugin development. They do it from different starting points: one optimizes for a broad productivity stack, the other for an explicit task lifecycle, deterministic registration and real-environment testing.

This is the comparison in this series where the tools really do overlap.

Both XrmFramework and Pillaro are actively maintained Dataverse development frameworks.

Both move plugin registration closer to code.

Both provide project structure and deployment tooling.

Both try to make Dataverse development more predictable than repeating raw SDK plumbing in every project.

But they are not trying to build the same product.

That makes a simple feature-count comparison less useful than it first appears.

The better question is:

What development model does each framework want your team to adopt?

Disclosure: I maintain Pillaro. I have tried to describe XrmFramework from its current repository and documentation, including the parts where it is clearly broader or more mature.

Pillaro is listed in Microsoft Learn's Community Tools for Microsoft Dataverse catalog and is published in Microsoft Marketplace. The Marketplace offer installs the Dataverse-side managed solution the framework depends on, including the model-driven app for configuration and diagnostic logs; development teams consume the C# framework through NuGet.

These listings improve discoverability and distribution, but the Microsoft Learn catalog explicitly states that community tools are supported by their publishers rather than by Microsoft.

Microsoft Learn listing · Microsoft Marketplace


The short version

If you want the comparison in one paragraph:

XrmFramework is the broader productivity platform. It includes typed model tooling, a service-oriented architecture, declarative plugin registration, Custom API deployment, Roslyn analyzers, project templates, web-resource deployment utilities and a live remote debugger.

Pillaro is narrower and more opinionated around the plugin execution lifecycle. It centers development around small tasks with explicit validation and execution phases, stable registration identity, deterministic deployment, structured diagnostics and integration tests that run against a real Dataverse environment.

Neither design is universally better.

They optimize different things.


XrmFramework's design center: remove Dataverse development friction

The XrmFramework README describes its goal as making Dynamics 365 / Dataverse development feel more like modern .NET.

That is visible across the project.

Plugin steps are declared in code

A plugin inherits from the framework Plugin base class and defines steps in AddSteps():

protected override void AddSteps()
{
    AddStep(
        Stages.PreValidation,
        Messages.Create,
        Modes.Synchronous,
        AccountDefinition.EntityName,
        nameof(Method1));

    AddStep(
        Stages.PostOperation,
        Messages.Update,
        Modes.Synchronous,
        AccountDefinition.EntityName,
        nameof(Method2));
}
Enter fullscreen mode Exit fullscreen mode

Additional registration metadata is attached to the target method with attributes:

[PreImage(AccountDefinition.Columns.Name)]
[PostImage(AccountDefinition.Columns.Name)]
[FilteringAttributes(
    AccountDefinition.Columns.Name,
    AccountDefinition.Columns.AccountNumber)]
[ExecutionOrder(100)]
public void Method(...)
{
}
Enter fullscreen mode Exit fullscreen mode

Deployment then registers those steps in Dataverse.

Strongly typed models are a major part of the experience

XrmFramework 3.1+ makes .table files the source of truth for Dataverse table metadata. Typed table, column and option-set definitions are generated at compile time by a Roslyn source generator.

Binding models can be described in .model files and generated the same way, including the mapping code between Dataverse entities and typed models.

The files can be produced and maintained through XrmFramework tooling, including the Definition Manager and the xrmframework CLI.

The design goal is clear:

reduce magic strings and make Dataverse metadata refactor-friendly.

The 3.1 line is a stable release on NuGet, and the xrmframework CLI includes a one-time migration path for 2.x solutions.

Business logic is organized into services

XrmFramework has an IService architecture for data access and business logic.

Those typed services can be injected into plugin methods, Custom APIs and external code.

That gives the framework a larger architectural surface than only the plugin entry point.

Custom APIs are first-class

The framework supports defining modern Dataverse Custom APIs from C# and deploying the related:

  • customapi,
  • customapirequestparameter,
  • customapiresponseproperty

metadata.

For teams using Custom APIs heavily, that is significant.

Remote debugging is a differentiator

XrmFramework includes a remote debugger that forwards a real Dataverse plugin execution to the developer's machine over Azure Relay.

You can set a Visual Studio breakpoint and step through the execution locally.

Pillaro does not have an equivalent feature.

The tooling surface is broad

The current project also includes:

  • xrmframework CLI scaffolding for solutions and projects,
  • deployment utilities,
  • Roslyn source generators, analyzers and code fixes,
  • TypeScript tooling,
  • web-resource deployment,
  • unit-test helpers,
  • tracing utilities.

XrmFramework has also been around for years and its NuGet packages have accumulated hundreds of thousands of downloads.

This is a mature, broad toolchain.


Pillaro's design center: make the execution model explicit

Pillaro started from a narrower set of problems.

The recurring issues we wanted to remove were:

  • plugin classes where validation and execution gradually became mixed,
  • registration that drifted between environments,
  • duplicate or stale plugin steps,
  • tests that did not verify the real deployed behavior,
  • and solutions where every plugin ended up with a slightly different internal structure.

The framework therefore makes the execution unit a Task.

A task has an explicit lifecycle

The simplified model is:

Plugin
  ↓
Task
  ├─ Validation
  └─ Execution
Enter fullscreen mode Exit fullscreen mode

A task declares its validations separately from its business logic:

public class MyFirstTask(
    IServiceProvider serviceProvider,
    TaskContext taskContext)
    : TaskBase<Logic.Task>(serviceProvider, taskContext)
{
    protected override ICompleteValidation AddValidations(
        IBasicModeValidation validator)
    {
        return validator
            .WithMode(PluginMode.Synchronous)
            .WithStage(PluginStage.Preoperation)
            .WithMessages(["Create"])
            .ForEntity(ContextEntity.LogicalName);
    }

    protected override void DoExecute()
    {
        // Business logic
    }
}
Enter fullscreen mode Exit fullscreen mode

The framework is deliberately opinionated here.

The split between validation and execution is not a convention that every team member has to remember. It is part of the model.

Runtime task registration and deployment registration are separate

The plugin decides which tasks participate in runtime execution (in these samples, Task is the Dataverse activity table, not the framework concept):

RegisterTask<MyFirstTask>(
    PluginStage.Preoperation,
    ["Create"],
    Task.EntityLogicalName,
    PluginMode.Synchronous);
Enter fullscreen mode Exit fullscreen mode

Deployment metadata is declared separately:

public override void Register(IPluginRegistration registration)
{
    registration
        .OnCreate<Task>("8c46d6e6-3c25-4b9d-9264-6c0d02b4d2f1")
        .PreOperation()
        .Synchronous()
        .Rank(1);
}
Enter fullscreen mode Exit fullscreen mode

The explicit step ID gives registration a stable identity across deployments.

Deployment is treated as synchronization

Pillaro's deployment tooling reads registration metadata from code and synchronizes the assembly, steps, images, filtering attributes, rank, configuration and solution membership.

The desired direction is:

Registration in code
        ↓
Compare with Dataverse
        ↓
Synchronize state
Enter fullscreen mode Exit fullscreen mode

That is intentionally different from a deployment model where code only adds what is missing.

Testing targets real Dataverse

Pillaro's test package is built around integration tests against an actual Dataverse environment with deployed plugins.

A test can:

  1. create tracked test data,
  2. trigger the real operation,
  3. let Dataverse execute the deployed plugin,
  4. verify the outcome,
  5. clean up test data.

That gives a different testing emphasis than a unit-test helper package.

Diagnostics are part of the runtime model

The framework includes execution-flow logging, task-level diagnostics, timings and depth information.

The design preference is to make production behavior inspectable without making live debugging the primary development path.

Pillaro does not provide XrmFramework-style remote debugging.


The comparison

Here is the current high-level picture.

Area XrmFramework Pillaro
Main design center Broad Dataverse developer productivity Explicit plugin/task lifecycle and repeatable delivery
Execution unit Plugin step routed to a method Plugin orchestrates one or more Tasks
Validation/execution split Team convention Explicit framework lifecycle
Step declaration AddStep(...) + method attributes Register(IPluginRegistration) fluent API
Stable explicit step ID Not the central public API model Required by registration API
Typed metadata .table / .model source files + Roslyn generation + XrmFramework tooling Early-bound helper tooling around pac modelbuilder
Service architecture Typed IService classes and injection Task/context model + framework services/features
Custom API tooling Custom API, parameters and response properties deployed from C# Main-operation tasks; the Custom API definition stays in the solution
Build-time diagnostics Roslyn analyzers and fixes No equivalent analyzer suite today
Live remote debugging Yes, over Azure Relay No
Runtime diagnostics Rich tracing Structured plugin/task diagnostics and performance data
Unit-test helpers Yes Framework focus is not mocked unit execution
Tests against real Dataverse Build your own approach Built-in integration testing stack
Web-resource tooling Yes No
Project scaffolding xrmframework new ... CLI dotnet new + Visual Studio VSIX
Plugin deployment Built-in deployment utilities Built-in synchronization/deployment tooling
Solution pack/unpack Not the core comparison Delegated to PAC CLI
License MIT Apache-2.0
Project maturity Established, years of usage Newer framework, first stable generation in 2026

There are deliberately no "winner" ticks in this table.

A feature only matters if your team needs it.


The biggest architectural difference

The most important distinction is not remote debugging or model generation.

It is where each framework places the center of gravity.

XrmFramework's center is the broader developer experience:

Typed models
Services
Plugin methods
Custom APIs
Analyzers
Deployment
Remote debugging
Web resources
Enter fullscreen mode Exit fullscreen mode

Pillaro's center is the execution and delivery model:

Plugin
  ↓
Task
  ├─ explicit validation
  └─ explicit execution
  ↓
registration as desired state
  ↓
deployment synchronization
  ↓
real-environment tests
Enter fullscreen mode Exit fullscreen mode

That difference affects how teams structure their code.


Typed metadata: two valid approaches

Both frameworks try to reduce raw logical-name strings, but the tooling is different.

XrmFramework

In XrmFramework 3.1+, .table files are the source of truth for table metadata. A Roslyn source generator turns them into typed *Definition classes and option-set enums at compile time.

The same model extends to binding models: .model files describe the typed shape and the generator emits the corresponding class and entity-mapping code.

The files can be created or refreshed from Dataverse through XrmFramework tooling such as the xrmframework CLI, while the generated C# does not need to be checked into source control.

This is a framework-owned typed metadata experience with a source-generation model.

Pillaro

Pillaro currently builds its typed entity workflow around Microsoft's Power Platform CLI.

The plugin package generates helper files under Tools/EarlyBound/, but the underlying generator is:

pac modelbuilder build
Enter fullscreen mode Exit fullscreen mode

That is a deliberate design choice.

Pillaro provides the project convention and wrapper; Microsoft owns the actual model generator.

Neither approach is inherently superior.

One provides a more integrated framework experience.

The other keeps the metadata generation boundary closer to first-party tooling.


Debugging vs observability

This is one of the clearest differences.

XrmFramework: debug the live execution

Its remote debugger is compelling when the team wants to:

  • reproduce a difficult runtime path,
  • attach a breakpoint,
  • inspect local state step by step,
  • debug against a real environment.

Nothing in Pillaro currently replaces that.

Pillaro: make executions inspectable

Pillaro takes a different direction:

  • structured execution flow,
  • task-level logging,
  • performance timing,
  • depth tracking,
  • repeatable real-environment tests.

The aim is to reduce the number of cases where a developer needs an attached debugger at all.

Those are different engineering preferences.

Some teams will want both ideas.


Testing: simulated isolation vs deployed behavior

Testing is another place where the word "testing" can hide very different things.

XrmFramework publishes XrmFramework.Tests, described in its package list as unit-test helpers for plugins and services.

That is useful when you want fast isolated feedback around framework code.

Pillaro's built-in testing stack intentionally does something else.

It requires a Dataverse environment with the plugin already deployed.

The test then creates or modifies actual Dataverse records and verifies the resulting behavior.

That makes the tests slower and dependent on an environment.

It also means they verify things a mocked test cannot fully verify:

  • actual registration,
  • actual pipeline execution,
  • platform behavior,
  • interactions between deployed plugins,
  • real Dataverse data operations.

A mature project may want both unit-level and integration-level coverage.

The frameworks simply emphasize different layers.


Breadth has a cost — and narrowness has a cost

XrmFramework's breadth is a benefit when you want one coherent stack.

It also means there is more framework to adopt:

  • model definitions,
  • service architecture,
  • deployment utilities,
  • analyzers,
  • debugger,
  • Custom API conventions,
  • potentially web-resource tooling.

If your team wants those things, the integration is valuable.

If your problem is only plugin structure and deterministic registration, it may be more surface than you need.

Pillaro has the opposite trade-off.

It does not currently provide:

  • a remote debugger,
  • XrmFramework's analyzer suite,
  • TypeScript/web-resource tooling,
  • Custom API definitions deployed from code (Pillaro runs the main-operation task, but the customapi records and their parameters stay in your solution),
  • the same years of ecosystem maturity.

If your team needs those features, that matters.

A smaller surface is only an advantage when the smaller surface contains the things you actually need.


When XrmFramework is a natural fit

XrmFramework deserves serious consideration when your team wants a broad Dataverse development platform and values several of these together:

  • framework-owned typed model tooling,
  • a service-oriented architecture across plugins and external code,
  • full Custom API tooling,
  • Roslyn diagnostics,
  • remote debugging,
  • web-resource deployment,
  • a mature package ecosystem,
  • years of production usage.

It is especially interesting when the goal is:

remove as much recurring Dataverse development friction as possible through one integrated framework.


When Pillaro is a natural fit

Pillaro is aimed more directly at teams that care about:

  • explicit task-sized units of business logic,
  • enforced validation → execution structure,
  • stable registration identity,
  • registration synchronized from code,
  • avoiding duplicate/stale plugin steps,
  • structured diagnostics,
  • integration tests against real Dataverse,
  • using Microsoft tooling such as pac modelbuilder where it already solves the problem.

The design goal is closer to:

keep the framework surface focused, but make the plugin lifecycle and delivery model highly explicit.


Migration cost matters more than feature tables

If you already have a large XrmFramework solution, this article is not an argument to migrate it.

A framework replacement changes more than NuGet packages.

It can affect:

  • architecture,
  • project structure,
  • registration,
  • tests,
  • deployment,
  • developer habits,
  • operational tooling.

A migration only makes sense when the destination solves a real problem that justifies that cost.

The same is true in the opposite direction.

If a Pillaro project needs capabilities that are central to XrmFramework, the feature gap should be evaluated honestly instead of forcing a smaller framework to become something it was not designed to be.


My conclusion

I do not think the useful conclusion is:

XrmFramework is for large projects and Pillaro is for small projects.

That is too simplistic.

A better distinction is:

XrmFramework optimizes for breadth of developer productivity.

Pillaro optimizes for explicitness of plugin architecture, deployment state and deployed-behavior verification.

Those priorities can exist in projects of very different sizes.

The right choice depends on which source of friction your team actually wants the framework to own.


What comes next

The final article in this series zooms out.

Instead of comparing frameworks, Part 4 maps the Dataverse plugin tooling stack in 2026:

  • what belongs to the SDK,
  • what PAC CLI now owns,
  • where PRT still fits,
  • what frameworks add,
  • and where older deployment tools such as spkl fit in a modern codebase.

Sources


Disclosure: I maintain Pillaro. XrmFramework descriptions in this article are based on its current public repository and documentation as reviewed in October 2026. Corrections are welcome.

Top comments (0)