Pdelvo.Dvx.Cli
0.1.0-ci.2
dotnet tool install --global Pdelvo.Dvx.Cli --version 0.1.0-ci.2
dotnet new tool-manifest
dotnet tool install --local Pdelvo.Dvx.Cli --version 0.1.0-ci.2
#tool dotnet:?package=Pdelvo.Dvx.Cli&version=0.1.0-ci.2&prerelease
nuke :add-package Pdelvo.Dvx.Cli --version 0.1.0-ci.2
dvx
Declarative deployment + SDK for Microsoft Dataverse / Dynamics 365 plugins. dvx replaces spkl-style imperative registration with a Terraform-like plan/apply model, and replaces hand-written plugin boilerplate with source-generated code.
You write only this, in a plain SDK-style csproj:
using Dvx;
using Dvx.Model;
public partial class AccountPlugins
{
[Step(typeof(Account), Msg.Update, Stage.PreOperation,
Filtering = new[] { Account.Cols.CreditLimit },
Key = "account-block-creditlimit-downgrade")]
public void BlockCreditLimitDowngrade(IStepContext<Account> ctx)
{
if (ctx.Target.CreditLimit < ctx.PreImage.CreditLimit)
throw ctx.Fail("Credit limits can't be lowered.");
}
}
Then run dotnet build -t:Deploy. Everything else is generated or computed:
- the
IPluginentry class and the embedded context runtime (no base classes, no runtime DLL, no ILMerge), - the pre-image registration — columns inferred from your code (
ctx.PreImage.CreditLimitabove ⇒ a pre-image with exactlycreditlimit); escapes are compile errors, never a silent "all columns", - the registration manifest, diffed field-by-field against the live environment.
A no-op deploy performs zero writes in ~5 read round trips. dvx plan shows exactly what would change
(--json for machines, exit code 0 = clean / 2 = changes pending) — put it in CI and never let an
environment drift again.
Feature overview
| Feature | How |
|---|---|
| Early-bound models | dvx model sync --entities account,contact snapshots metadata into dataverse.snapshot.json (committed); a source generator emits entity classes, Cols constants, and optionset enums in-memory — no generated .cs files on disk |
| Plugin steps | [Step(typeof(Account), Msg.Update, Stage.PreOperation, ...)] on a plain method taking IStepContext<Account> |
| Images | Inferred from ctx.PreImage.X / ctx.PostImage.X access; explicit [Image(...)] as the escape hatch |
| Custom APIs | [CustomApi("dvx_Reverse")] on a method with record request/response types; parameters sync automatically; typed callers are generated |
| Computed columns | [Derive(Account.Cols.Description)] on a pure static method — compiles to coalesced, dependency-ordered Create/Update steps with inferred filtering |
| Deployment | dvx plan / dvx apply (or dotnet build -t:Plan / -t:Deploy): field-level diff, sha256 assembly-upload skip, --prune for env-only components |
| Guard rails | 20+ compile-time diagnostics (DVX0xx): invalid stage/mode combos, pre-image on Create, filtering on unsupported messages, duplicate keys, impure derives, … |
Getting started
- New project: docs/getting-started.md
- Migrating from spkl: docs/migrating-from-spkl.md
- Runtime API contract: docs/dvx-context-api-spec.md
- Planned work: docs/backlog.md
The five-minute version
# 1. install the CLI (or run it from source: dotnet run --project src/Dvx.Cli --)
dotnet tool install --global Pdelvo.Dvx.Cli --prerelease
# 2. in your plugin repo: describe your environments (no secrets — this file is committed)
cat > dataverse.yaml <<'EOF'
default: dev
environments:
dev:
url: https://yourorg.crm.dynamics.com
solution: yoursolution
prefix: your
EOF
# 3. sign in once (device code; later runs are silent)
dvx auth login
dvx whoami
# 4. snapshot the entities you use
dvx model sync --entities account,contact
# 5. build & deploy
dotnet build -t:Deploy
Architecture in one paragraph
Plugins target net462 and reference exactly one package, Dvx.Sdk (attributes + source generators +
MSBuild targets). The generators are pure: metadata comes exclusively from the committed
dataverse.snapshot.json, so builds are reproducible and CI needs no Dataverse connection. The
registration manifest is emitted as an inactive preprocessor block — it contributes zero IL, so a
registration-only change (a rank, a filter column) never changes the assembly hash and never re-uploads
the DLL. The dvx CLI talks raw Dataverse Web API (no Microsoft.PowerPlatform.Dataverse.Client
dependency), reads the environment in a constant number of queries, canonicalizes both sides, and
PATCHes only changed columns. Matching precedence for existing steps: explicit Id → Key → natural key
(plugin type, message, entity, stage) when unique on both sides → name; ambiguity is a hard error,
never a heuristic.
Repository layout
src/Dvx.Cli/ the dvx CLI (net9.0, packs as dotnet tool Dvx.Cli)
src/Dvx.Sdk/ attributes (netstandard2.0) — the package plugins reference
src/Dvx.Sdk.Generators/ Roslyn incremental generators + diagnostics (ships inside Dvx.Sdk)
src/Dvx.Sdk.Targets/ MSBuild props/targets (ship inside Dvx.Sdk)
tests/ unit, generator, and live-environment integration tests
samples/TestPlugins/ end-to-end sample plugin project
docs/ getting started, spkl migration, API spec, backlog
Building
dotnet build && dotnet test
Integration tests run only when DVX_TEST_URL is set and create only prefixed, torn-down test
components. CI publishes prerelease packages to NuGet as unlisted versions.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|