Rystem.RepositoryFramework.Infrastructure.Dynamics.Dataverse 10.0.7

dotnet add package Rystem.RepositoryFramework.Infrastructure.Dynamics.Dataverse --version 10.0.7
                    
NuGet\Install-Package Rystem.RepositoryFramework.Infrastructure.Dynamics.Dataverse -Version 10.0.7
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Rystem.RepositoryFramework.Infrastructure.Dynamics.Dataverse" Version="10.0.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Rystem.RepositoryFramework.Infrastructure.Dynamics.Dataverse" Version="10.0.7" />
                    
Directory.Packages.props
<PackageReference Include="Rystem.RepositoryFramework.Infrastructure.Dynamics.Dataverse" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Rystem.RepositoryFramework.Infrastructure.Dynamics.Dataverse --version 10.0.7
                    
#r "nuget: Rystem.RepositoryFramework.Infrastructure.Dynamics.Dataverse, 10.0.7"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Rystem.RepositoryFramework.Infrastructure.Dynamics.Dataverse@10.0.7
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Rystem.RepositoryFramework.Infrastructure.Dynamics.Dataverse&version=10.0.7
                    
Install as a Cake Addin
#tool nuget:?package=Rystem.RepositoryFramework.Infrastructure.Dynamics.Dataverse&version=10.0.7
                    
Install as a Cake Tool

Rystem.RepositoryFramework.Infrastructure.Dynamics.Dataverse

Rystem.RepositoryFramework.Infrastructure.Dynamics.Dataverse adds a Microsoft Dataverse adapter for Repository Framework.

It auto-provisions a Dataverse table for your model, but the storage model is much simpler than a typical Dataverse-first mapping: every repository property is stored as a string column, and most queries run in memory after fetch.

Installation

dotnet add package Rystem.RepositoryFramework.Infrastructure.Dynamics.Dataverse

Architecture

For each registered model, the package works with one Dataverse table.

  • table logical name: Prefix + TableName.ToLower()
  • primary attribute logical name: Prefix + PrimaryKey.ToLower()
  • every model property becomes a Dataverse string attribute
  • primitive values are stringified
  • non-primitive values are JSON serialized

So this package uses Dataverse mainly as a managed record store, not as a strongly typed Dataverse-schema translator.

Registration APIs

Available on all three patterns:

  • WithDataverse(...)

Supported for:

  • IRepositoryBuilder<T, TKey>
  • ICommandBuilder<T, TKey>
  • IQueryBuilder<T, TKey>

Example

This matches the repository API tests.

builder.Services.AddRepository<CalamityUniverseUser, string>(repositoryBuilder =>
{
    repositoryBuilder.WithDataverse(dataverseBuilder =>
    {
        dataverseBuilder.Settings.Prefix = "repo_";
        dataverseBuilder.Settings.SolutionName = "Solution001";
        dataverseBuilder.Settings.TableName = "CalamityUniverseUser";

        dataverseBuilder.Settings.SetConnection(
            builder.Configuration["Dataverse:Environment"]!,
            new DataverseAppRegistrationAccount(
                builder.Configuration["Dataverse:ClientId"]!,
                builder.Configuration["Dataverse:ClientSecret"]!));
    });
});

Example model from the tests:

public sealed class CalamityUniverseUser
{
    public string? Id { get; set; }
    public string? Name { get; set; }
    public string? Email { get; set; }
    public int Port { get; set; }
    public bool IsAdmin { get; set; }
    public Guid GroupId { get; set; }
}

Builder API

IDataverseRepositoryBuilder<T, TKey> exposes:

Member Purpose
Settings Connection and Dataverse table settings
WithColumn(expr, customPrefix) Override the prefix used for one property

DataverseOptions<T, TKey> exposes:

Property Default Notes
Prefix new_ Global prefix for the logical table and column names
TableName typeof(T).Name Display and schema base name
PrimaryKey Id Name of the Dataverse primary attribute used to store the repository key
SolutionName null Used when creating a new table
Description null Falls back to an auto-generated description
Environment required Set through SetConnection(...)
ApplicationIdentity required Set through SetConnection(...)

Connection configuration

Use:

dataverseBuilder.Settings.SetConnection(
    environment,
    new DataverseAppRegistrationAccount(clientId, clientSecret));

Internally the package creates a ServiceClient with a client-secret connection string like:

Url=https://{Environment}.dynamics.com;AuthType=ClientSecret;ClientId=...;ClientSecret=...;RequireNewInstance=true

This package currently documents and implements only the app-registration client-secret path.

Warm-up and provisioning

WithDataverse(...) automatically registers a warm-up action.

At startup, BootstrapAsync():

  1. checks whether the Dataverse table exists
  2. creates it if missing
  3. checks every model property column
  4. creates any missing columns as StringAttributeMetadata

You should still run:

await app.Services.WarmUpAsync();

That is the path the repository expects for auto-provisioning.

What gets provisioned

When the table is created:

  • ownership type is UserOwned
  • the primary Dataverse attribute is a string column with max length 100

When model properties are created:

  • primitive properties become string columns with max length 100
  • non-primitive properties become string columns with max length 2000

So even if your .NET property is int, bool, Guid, or a complex object, the stored Dataverse attribute is still a string attribute.

Key behavior

The repository key TKey is stored in the Dataverse primary attribute column.

  • primitive TKey values are stored with ToString()
  • non-primitive TKey values are stored as JSON

Important nuance:

  • PrimaryKey config controls the Dataverse primary attribute name
  • it does not automatically mean the same property on your model is the repository key source

For example, the tests register CalamityUniverseUser with TKey = string, while the Dataverse table still uses the default logical primary attribute based on Id.

Query behavior

QueryAsync(...) is mostly client-side.

The repository issues a Dataverse query with:

  • TopCount = 100
  • the configured ColumnSet
  • no translated Repository Framework filter expression

Then it:

  1. materializes the returned Dataverse entities into models
  2. rebuilds Entity<T, TKey> values
  3. applies Repository Framework filters in memory

Practical consequence:

  • Where, ordering, paging, and aggregates all operate on the fetched in-memory subset
  • only the first 100 Dataverse rows are visible to QueryAsync(...)

CRUD behavior

Key-based methods do use Dataverse-side filtering.

  • GetAsync, ExistAsync, UpdateAsync, and DeleteAsync query by the logical primary key column
  • InsertAsync creates a new Dataverse row directly

UpdateAsync first finds the existing Dataverse row and then updates it by Dataverse row id.

Batch and aggregate behavior

  • BatchAsync(...) is a sequential loop over insert/update/delete operations
  • there is no Dataverse bulk pipeline or transaction support in this package
  • aggregate operations such as Count, Sum, Min, Max, and Average run in memory after QueryAsync(...)

Custom column prefix caveat

WithColumn(x => x.Property, customPrefix: "...") exists, but the current implementation is less reliable than the API suggests.

Important source-backed caveats:

  • column existence checks during bootstrap primarily look for unprefixed or globally prefixed names
  • ColumnSet is built before WithColumn(...) overrides the property prefix

So per-column custom prefixes may not behave consistently across provisioning and reads. If you need robust mappings, prefer a single global Prefix for the whole table.

Solution behavior caveat

SolutionName is used when creating a new Dataverse table.

The current implementation does not perform a broader ongoing solution-management workflow for already existing tables or attributes.

CQRS examples

builder.Services.AddCommand<AccountModel, string>(commandBuilder =>
{
    commandBuilder.WithDataverse(dataverseBuilder =>
    {
        dataverseBuilder.Settings.Prefix = "repo_";
        dataverseBuilder.Settings.SetConnection(
            builder.Configuration["Dataverse:Environment"]!,
            new DataverseAppRegistrationAccount(
                builder.Configuration["Dataverse:ClientId"]!,
                builder.Configuration["Dataverse:ClientSecret"]!));
    });
});

builder.Services.AddQuery<AccountModel, string>(queryBuilder =>
{
    queryBuilder.WithDataverse(dataverseBuilder =>
    {
        dataverseBuilder.Settings.Prefix = "repo_";
        dataverseBuilder.Settings.SetConnection(
            builder.Configuration["Dataverse:Environment"]!,
            new DataverseAppRegistrationAccount(
                builder.Configuration["Dataverse:ClientId"]!,
                builder.Configuration["Dataverse:ClientSecret"]!));
    });
});

When to use this package

Use it when you want:

  • Dataverse-backed persistence behind Repository Framework abstractions
  • automatic table and column provisioning at startup
  • a simple storage layer where complex values can be serialized into Dataverse string attributes

Avoid it when you need deep Dataverse-native query translation or strongly typed Dataverse attribute mapping, because the current implementation is intentionally much thinner than that.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.7 38 3/26/2026
10.0.6 180,026 3/3/2026
10.0.5 54 2/22/2026
10.0.4 62 2/9/2026
10.0.3 147,859 1/28/2026
10.0.1 209,028 11/12/2025
9.1.3 204 9/2/2025
9.1.2 764,432 5/29/2025
9.1.1 97,789 5/2/2025
9.0.32 186,655 4/15/2025
9.0.31 5,760 4/2/2025
9.0.30 88,812 3/26/2025
9.0.29 9,005 3/18/2025
9.0.28 203 3/17/2025
9.0.27 236 3/16/2025
9.0.26 230 3/13/2025
9.0.25 52,093 3/9/2025
9.0.21 271 3/6/2025
9.0.20 19,503 3/6/2025
9.0.19 263 3/6/2025
Loading failed