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
<PackageReference Include="Len.StronglyTypedId.Generators" Version="1.4.1" />
<PackageVersion Include="Len.StronglyTypedId.Generators" Version="1.4.1" />
<PackageReference Include="Len.StronglyTypedId.Generators" />
paket add Len.StronglyTypedId.Generators --version 1.4.1
#r "nuget: Len.StronglyTypedId.Generators, 1.4.1"
#:package Len.StronglyTypedId.Generators@1.4.1
#addin nuget:?package=Len.StronglyTypedId.Generators&version=1.4.1
#tool nuget:?package=Len.StronglyTypedId.Generators&version=1.4.1
StronglyTypedId
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.
SystemTextJsonandNewtonsoftJsonconverters via a generated[JsonConverter]. - Binary serialization.
MessagePackandMongoDb: a generatedIMessagePackFormatter<TId>/IBsonSerializer<TId>, wired up byMessagePackSerializerOptions.AddStronglyTypedIds()andStronglyTypedIds.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 isBigInteger,Int128andUInt128, 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()fromConfigureConventions, plus a model-scanningmodelBuilder.AddStronglyTypedIds()fromOnModelCreatingfor contexts that do not overrideConfigureConventions. Both overloads are generated whenever EF Core ≥ 7.0.0 is referenced. - Dapper.
Dapper:StronglyTypedIds.AddStronglyTypedIds()registersTypeHandlers in Dapper's process-wide registry, so it only needs to run once. TheAddStronglyTypedIds(IDbConnection)overload is kept for source compatibility and ignores its argument. - Swashbuckle / OpenAPI.
SwaggerandAspNetCoreOpenApi:options.AddStronglyTypedIds()forAddSwaggerGen/AddOpenApi. - ASP.NET Core MVC.
AspNetCoreMvc:options.AddStronglyTypedIds()enables[FromRoute] OrderId idand{id:OrderId}. - GraphQL.
HotChocolate: a generatedScalarType<TId>per id, wired up byAddStronglyTypedIds()onIRequestExecutorBuilder. 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 GraphQLString, integers and enums areInt(enums by underlying value, not member name), and floating points areFloat. The entry point is idempotent and usesBindRuntimeType, 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, soSerializer.Serialize(order)works with no registration call. The payload is the primitive value itself;Int128,UInt128,Half,DateTimeOffset,BigIntegerand 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
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 |