Len.StronglyTypedId.Generators 1.4.1

dotnet add package Len.StronglyTypedId.Generators --version 1.4.1
                    
NuGet\Install-Package Len.StronglyTypedId.Generators -Version 1.4.1
                    
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="Len.StronglyTypedId.Generators" Version="1.4.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Len.StronglyTypedId.Generators" Version="1.4.1" />
                    
Directory.Packages.props
<PackageReference Include="Len.StronglyTypedId.Generators" />
                    
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 Len.StronglyTypedId.Generators --version 1.4.1
                    
#r "nuget: Len.StronglyTypedId.Generators, 1.4.1"
                    
#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 Len.StronglyTypedId.Generators@1.4.1
                    
#: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=Len.StronglyTypedId.Generators&version=1.4.1
                    
Install as a Cake Addin
#tool nuget:?package=Len.StronglyTypedId.Generators&version=1.4.1
                    
Install as a Cake Tool

StronglyTypedId

NuGet License: MIT

Len.StronglyTypedId is a Roslyn source generator (with analyzer and code fixes) that turns a one-line record declaration into a complete strongly typed identifier. It generates the IStronglyTypedId<TSelf, TPrimitiveId> and IParsable<TSelf> implementation plus System.Text.Json / Newtonsoft.Json / EF Core / Dapper / Swashbuckle / ASP.NET Core / MessagePack / MongoDB / HotChocolate / protobuf-net integrations, so a Guid can never be passed where an OrderId is expected, or the reverse.

Install

dotnet add package Len.StronglyTypedId

One package is enough, because it already depends on Len.StronglyTypedId.Generators. Its target frameworks are net8.0 and net10.0, so the package works in any project targeting either one. Integrations are generated only when the matching package is referenced.

Quick start

[StronglyTypedId]
public partial record struct OrderId(Guid Value);

var id = OrderId.Create(Guid.NewGuid());           // static factory
var parsed = OrderId.Parse("...", null);           // IParsable<TSelf>; a null provider means the invariant culture
OrderId.TryParse("...", null, out var alsoParsed); // false instead of throwing

Supported primitive types: Guid, string, char, byte, sbyte, short, ushort, int, uint, long, ulong, Int128, UInt128, decimal, Half, float, double, DateTime, DateTimeOffset, TimeSpan, DateOnly, TimeOnly, BigInteger, and any enum. Use record struct for a value type, record for a reference type.

Point Validator at a static method to reject invalid values:

[StronglyTypedId(Validator = nameof(Validate))]
public partial record struct OrderId(Guid Value)
{
    private static bool Validate(Guid value) => value != Guid.Empty;
}

Point Normalizer at a static method to repair values before they are validated and stored: trim whitespace, fold case, drop separators.

[StronglyTypedId(Normalizer = nameof(Normalize), Validator = nameof(IsEmail))]
public partial record struct EmailAddress(string Value)
{
    private static string Normalize(string value) => value.Trim().ToLowerInvariant();

    private static bool IsEmail(string value) => value.Contains('@');
}

EmailAddress.Create("  Alice@Example.COM  ").Value; // "alice@example.com"

Swap the exception Create throws when the validator rejects a value, and declare named static instances:

[StronglyTypedId(Validator = nameof(Validate), ExceptionType = typeof(DomainRuleViolationException))]
[StronglyTypedIdInstance("Unspecified", "-1")]
public partial record struct TenantId(int Value)
{
    private static bool Validate(int value) => value != 0;
}

var tenant = TenantId.Unspecified;   // public static readonly, built through Create

Instances are constructed through Create, so they honour the normalizer and validator and Parse(instance.ToString()) always reads back.

Give an id a canonical text form with Prefix ("ord_3f2c…"), choose the JSON shape with JsonShape, and narrow the generated integrations per id with Integrations:

[StronglyTypedId(Prefix = "ord_", JsonShape = JsonShape.Object,
                 Integrations = StronglyTypedIdIntegrations.Dapper | StronglyTypedIdIntegrations.SystemTextJson)]
public partial record struct OrderId(Guid Value);

Integrations

Generated only when the matching package is referenced; Integrations can narrow the set per id, and [assembly: StronglyTypedIdDefaults(...)] sets assembly-wide defaults. The flags are EntityFrameworkCore, Dapper, SystemTextJson, NewtonsoftJson, Swagger, AspNetCoreOpenApi, AspNetCoreMvc, MessagePack, MongoDb, HotChocolate and Protobuf:

  • Serialization. SystemTextJson and NewtonsoftJson converters via a generated [JsonConverter].
  • Binary serialization. MessagePack and MongoDb: a generated IMessagePackFormatter<TId> / IBsonSerializer<TId>, wired up by MessagePackSerializerOptions.AddStronglyTypedIds() and StronglyTypedIds.RegisterBsonSerializers(). Both write the primitive value itself; the text prefix belongs to the text form (ToString() / JSON) and does not reach the payload. The exception is BigInteger, Int128 and UInt128, which have no native MessagePack / BSON representation, so those ids are stored as their invariant-culture text, prefix included. Custom primitives take the same text path, because neither library has a formatter for a type it does not know.
  • EF Core. EntityFrameworkCore: configurationBuilder.AddStronglyTypedIds() from ConfigureConventions, plus a model-scanning modelBuilder.AddStronglyTypedIds() from OnModelCreating for contexts that do not override ConfigureConventions. Both overloads are generated whenever EF Core ≥ 7.0.0 is referenced.
  • Dapper. Dapper: StronglyTypedIds.AddStronglyTypedIds() registers TypeHandlers in Dapper's process-wide registry, so it only needs to run once. The AddStronglyTypedIds(IDbConnection) overload is kept for source compatibility and ignores its argument.
  • Swashbuckle / OpenAPI. Swagger and AspNetCoreOpenApi: options.AddStronglyTypedIds() for AddSwaggerGen / AddOpenApi.
  • ASP.NET Core MVC. AspNetCoreMvc: options.AddStronglyTypedIds() enables [FromRoute] OrderId id and {id:OrderId}.
  • GraphQL. HotChocolate: a generated ScalarType<TId> per id, wired up by AddStronglyTypedIds() on IRequestExecutorBuilder. The representation matches the JSON form, so an id is not written one way over REST and another over GraphQL: text primitives (including prefixed ids) are GraphQL String, integers and enums are Int (enums by underlying value, not member name), and floating points are Float. The entry point is idempotent and uses BindRuntimeType, without which HotChocolate would infer the id as an object type and the schema would fail to build.
  • protobuf-net. Protobuf: [ProtoContract(Surrogate = …)] on the id plus a nested {TypeName}Surrogate, so Serializer.Serialize(order) works with no registration call. The payload is the primitive value itself; Int128, UInt128, Half, DateTimeOffset, BigInteger and custom primitives have no native protobuf representation and are stored as canonical text. Ids from a referenced assembly get no surrogate, because the attribute can only be applied in the declaring compilation.

Testing

Len.StronglyTypedId.Testing builds ids for arrange blocks (Any.Id<OrderId>(), Any.Ids<OrderId>(10)), and Len.StronglyTypedId.Testing.AutoFixture / Len.StronglyTypedId.Testing.Bogus wire the same generator into AutoFixture (fixture.WithStronglyTypedIds()) and Bogus (faker.WithStronglyTypedIds()). All of them respect generated validators.

Native AOT & trimming

The runtime library is marked <IsAotCompatible>true</IsAotCompatible>: the core types and every generated converter avoid dynamic code generation and loading types by name, so strongly typed ids work in Native AOT and trimmed applications. The only reflection entry points (StronglyTypedIdExtensions.TryGetPrimitiveIdType, GetPrimitiveIdType and IsStronglyTypedId) are annotated with [DynamicallyAccessedMembers(Interfaces)] so the interface metadata that identifies a strongly typed id is preserved under trimming.

Changelog

Every release, including previews, is documented in CHANGELOG.md.

License

MIT License.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Len.StronglyTypedId.Generators:

Package Downloads
Len.StronglyTypedId

A source generator, analyzer, and code fix provider that turns a one-line record declaration into a fully featured strongly typed id, with built-in System.Text.Json, Newtonsoft.Json, Entity Framework Core, Dapper, Swashbuckle, and ASP.NET Core OpenAPI integrations.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.4.1 43 11/6/2026
1.4.0 155 10/30/2026
1.3.0 129 10/7/2026
1.3.0-preview.11 65 10/2/2026
1.3.0-preview.10 74 10/2/2026
1.3.0-preview.9 67 10/2/2026
1.3.0-preview.8 72 10/2/2026
1.3.0-preview.7 87 9/20/2026
1.3.0-preview.6 80 9/20/2026
1.3.0-preview.5 148 9/13/2026
1.3.0-preview.4 76 9/13/2026
1.2.1 2,320 2/26/2024
1.2.0 1,354 11/27/2023
1.1.3 1,515 10/8/2023
1.1.2 1,559 10/8/2023
1.1.1 1,817 5/20/2023
1.1.0 1,898 5/18/2023