EricksonLopez.DomainPrimitives.AspNetCore.SourceGenerators 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package EricksonLopez.DomainPrimitives.AspNetCore.SourceGenerators --version 1.0.0
                    
NuGet\Install-Package EricksonLopez.DomainPrimitives.AspNetCore.SourceGenerators -Version 1.0.0
                    
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="EricksonLopez.DomainPrimitives.AspNetCore.SourceGenerators" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EricksonLopez.DomainPrimitives.AspNetCore.SourceGenerators" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="EricksonLopez.DomainPrimitives.AspNetCore.SourceGenerators" />
                    
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 EricksonLopez.DomainPrimitives.AspNetCore.SourceGenerators --version 1.0.0
                    
#r "nuget: EricksonLopez.DomainPrimitives.AspNetCore.SourceGenerators, 1.0.0"
                    
#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 EricksonLopez.DomainPrimitives.AspNetCore.SourceGenerators@1.0.0
                    
#: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=EricksonLopez.DomainPrimitives.AspNetCore.SourceGenerators&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=EricksonLopez.DomainPrimitives.AspNetCore.SourceGenerators&version=1.0.0
                    
Install as a Cake Tool

EricksonLopez.DomainPrimitives πŸ›‘οΈ

NuGet NuGet Downloads CI Coverage Mutation Score License: MIT .NET NativeAOT

DomainPrimitives is a BCL-native, AOT-first domain primitive library for .NET 8+. It uses Roslyn Incremental Source Generators to produce strictly valid, immutable domain types with the deepest BCL interface coverage in the .NET ecosystem β€” including IUtf8SpanParsable<T>, ISpanFormattable, and IUtf8SpanFormattable.

Stop writing validation boilerplate. DomainPrimitives generates strongly-typed, BCL-native domain types from a single attribute β€” with built-in security gates, zero-alloc paths, and full Native AOT support out of the box.

πŸ†š Why DomainPrimitives?

Capability DomainPrimitives Vogen Thinktecture StronglyTypedId
IUtf8SpanParsable<T> generated (NET8+) βœ… ❌ ❌ ❌
ISpanFormattable generated βœ… ❌ β€” ❌
IUtf8SpanFormattable generated βœ… ❌ ❌ ❌
Declarative normalization ([Trim], [LowerCase]...) βœ… ❌ ❌ ❌
NFC Unicode normalization (SEC-004) βœ… ❌ ❌ ❌
ReDoS-resistant regex (NonBacktracking + 100ms) βœ… ❌ ❌ ❌
30 semantic domain type shortcuts βœ… ❌ ❌ ❌
Auto-discovered EF Core & Dapper (no annotations) βœ… ❌ ❌ ❌
Multi-property Value Object βœ… ❌ βœ… ❌
Smart Enum (source-generated, AOT-safe) βœ… ❌ βœ… ❌
TryCreate(out result, out error) (zero-alloc success) βœ… ❌ ❌ ❌
Native AOT compatible βœ… βœ… βœ… βœ…

Table notes: β€” means the library does not expose this capability as part of its generated surface (not tested, not documented, or explicitly out of scope for that library).
Not in DomainPrimitives yet: Discriminated Unions (Thinktecture only), Newtonsoft.Json converters (Vogen/StronglyTypedId), class-based primitives. See docs/feature-gaps.md for the full gap list.

πŸ“¦ Supported Primitives

Type Description Example
[StringPrimitive] String-backed primitive with validation pipeline. FirstName, Description
[NumericPrimitive<T>] Numeric-backed (int, decimal, double, etc.). Age, Money, Score
[DatePrimitive] Date-backed (DateOnly, DateTime). BirthDate, ExpirationDate
[StrongId] Strongly-typed IDs (Guid, int, long, string). UserId, OrderId
[ValueObject] Multi-property immutable value objects. Address, Money
[SmartEnum] Strongly-typed enums with behavior and AOT-safe static list. OrderStatus, Role

🎯 Semantic Shortcut Attributes

Instead of repeating validations, use built-in shortcut attributes that combine validation and normalization rules:

String shortcuts (15 types):

  • Identity: [Email], [Username], [PasswordHash]
  • Network: [Url], [IPAddress], [MacAddress]
  • Commerce: [Phone], [CountryCode], [CurrencyCode], [LanguageCode], [IBAN]
  • Content: [Slug], [HexColor], [ISBN], [VIN]

Numeric shortcuts (15 types): [Money], [Percentage], [Latitude], [Longitude], [Age], [Weight], [Height], [Distance], [Temperature], [Score], [Quantity], [Price], [TaxRate], [Discount], [Rating]

⚑ Quick Start

dotnet add package EricksonLopez.DomainPrimitives
using EricksonLopez.DomainPrimitives;

// 1. Define your primitive
[CountryCode] // Implies: [StringPrimitive], [Trim], [UpperCase], [Length(2, 2)]
public readonly partial record struct CountryIsoCode;

// 2. Create β€” throws DomainPrimitiveValidationException on invalid input
var code = CountryIsoCode.Create("  us  "); // Output: CountryIsoCode { Value = "US" } (trimmed + uppercased)

// 3. TryCreate β€” out-based, zero allocation on success
if (CountryIsoCode.TryCreate("us", out var validCode, out var error))
    Console.WriteLine($"Valid: {validCode}");  // Output: Valid: US
else
    Console.WriteLine($"Error [{error.Code}]: {error.Message}");

// 4. Parse from Span<char> β€” allocation-minimized path
if (CountryIsoCode.TryParse("us".AsSpan(), null, out var parsedCode))
    Console.WriteLine($"Parsed: {parsedCode}"); // Output: Parsed: US

// 5. Parse from UTF-8 bytes β€” native for HTTP/gRPC/Kafka scenarios (NET8+)
ReadOnlySpan<byte> utf8 = "us"u8;
if (CountryIsoCode.TryParse(utf8, null, out var utf8Code))
    Console.WriteLine($"UTF-8 parsed: {utf8Code}"); // Output: UTF-8 parsed: US

πŸ” Security Gates

DomainPrimitives is the only domain primitive library with built-in security gates applied automatically to all string types:

Gate Rule Protection
SEC-001 Default 4096-character limit on all string types without explicit MaxLength Prevents memory exhaustion attacks
SEC-002 RegexOptions.NonBacktracking on .NET 7+ Eliminates ReDoS vulnerabilities
SEC-003 100ms regex timeout on older TFMs Caps worst-case regex time
SEC-004 NFC Unicode normalization on all string inputs before validation Prevents Unicode homoglyph attacks
SEC-005 No PII echoed in error messages on sensitive types Prevents information leakage
SEC-006 Stackalloc ≀ 256 chars on char-span path; ≀ 256 bytes on UTF-8 byte path; ArrayPool<char> for larger inputs Prevents stack overflow on large inputs

⚑ Performance & Benchmarks

BenchmarkDotNet v0.15.8 Β· .NET 10.0.10 (10.0.1026.32716) Β· AMD Ryzen 7 9800X3D

DomainPrimitives is built with extreme performance and zero-allocation in mind. See the full benchmark methodology and results.

Hot Path Benchmarks

Benchmark Mean Allocated Zero-alloc?
RawGuid (baseline β€” no wrapper) 0.00 ns 0 B βœ…
PrimitiveGuid.Create(Guid) 0.00 ns 0 B βœ… Same as raw
PrimitiveGuid.TryParse(string) 12.63 ns 0 B βœ… Zero allocation
EmailAddress.Create(string) 49.53 ns 0 B βœ… Zero allocation
EmailAddress JSON serialize 102.34 ns 64 B ⚠️ JSON infra
EmailAddress JSON deserialize 95.58 ns 120 B ⚠️ JSON infra

Note: JSON allocation is from the Utf8JsonReader/Utf8JsonWriter infrastructure, not from the domain primitive itself. The TryParse hot path (called internally during deserialization) is zero-allocation.

vs. Industry Competitors (StrongId<Guid>)

Method Create Parse Allocated
Raw Guid (baseline) 0.00 ns 15.32 ns 0 B
DomainPrimitives 0.17 ns 15.81 ns 0 B
Vogen 0.01 ns 15.22 ns 0 B
StronglyTypedId 0.00 ns 15.29 ns 0 B
ValueOf 2.51 ns 16.99 ns 32 B

Results show DomainPrimitives maintains zero-allocation in hot paths and performs virtually identically to raw Guid and other struct-based generators, while avoiding the heap allocation overhead seen in class-based wrappers (e.g., ValueOf).

Allocation Model Audit

DomainPrimitives minimizes heap allocations in hot paths. Here is the honest per-path allocation audit:

Path Allocations Notes
TryCreate(string) β€” success, no normalization 0 Zero new heap objects
TryCreate(string) β€” success, with normalization 1 NFC .Normalize(FormC) β€” required by SEC-004
TryCreate(string) β€” failure 1 Error message string
TryParse(ReadOnlySpan<char>) ≀ 256 chars 1 stackalloc + 1 string for NFC + storage
TryParse(ReadOnlySpan<char>) > 256 chars 1 + pool ArrayPool<char> + 1 string
TryParse(ReadOnlySpan<byte>) ≀ 256 chars (NET8+) 1 stackalloc decode + 1 string
JSON deserialize via Utf8JsonReader.ValueSpan 1 Direct span read + 1 string for NFC + storage
TryFormat(Span<char>) β€” formatting 0 Writes into caller-provided span
EF Core materialization (struct types) 0 ValueConverter β€” no boxing for structs

Why 1 unavoidable allocation? Unicode NFC normalization (SEC-004) requires producing a System.String β€” normalization can change character count (combining characters β†’ composed), so the result cannot be stored as a span. The stored domain value is always NFC-normalized, which is correct and prevents homoglyph attacks.

No Result<T> overhead. TryCreate(out result, out error) is zero-allocation on the success path because both result (a struct) and error (a struct) live on the caller's stack. Unlike Result<T> wrapper patterns, no heap object is created.

🧩 Ecosystem Integrations

DomainPrimitives provides seamless integration via dedicated packages. Converters are auto-discovered β€” no per-type attributes needed in your domain layer:

Package Integration Auto-discovery
EricksonLopez.DomainPrimitives.AspNetCore Model binding, route params, OpenAPI βœ…
EricksonLopez.DomainPrimitives.EFCore ValueConverter for all domain types βœ…
EricksonLopez.DomainPrimitives.Dapper SqlMapper.TypeHandler for all domain types βœ…
EricksonLopez.DomainPrimitives.Mapster Mapster type mapping for composite ValueObjects (source-generated) βœ…
EricksonLopez.DomainPrimitives.OpenApi Swagger/OpenAPI schema filters βœ…
EricksonLopez.DomainPrimitives.Testing Assertions, builders, fakes for xUnit β€”

Known gap: Newtonsoft.Json converters are not yet supported. If your project uses Newtonsoft.Json, consider Vogen or StronglyTypedId for this scenario. Track progress in docs/feature-gaps.md.

Mapster note: For scalar primitives ([StringPrimitive], [StrongId], [NumericPrimitive<T>]), Mapster resolves the generated explicit operator automatically β€” no package needed. Add EricksonLopez.DomainPrimitives.Mapster only when mapping composite [ValueObject] types or when using Mapster in AOT source-generation mode. See ADR-017.

πŸ—οΈ Architecture

Target Framework Requirements

Package Minimum TFM Notes
EricksonLopez.DomainPrimitives net8.0 Full feature set: generators, analyzers, integrations.
EricksonLopez.DomainPrimitives.Abstractions netstandard2.0 Attributes, interfaces, and PrimitiveError only. No generators.
EricksonLopez.DomainPrimitives.Generators netstandard2.0 Source Generator β€” compile-time only, no runtime reference needed.

Minimum runtime: net8.0. IUtf8SpanParsable<T>, RegexOptions.NonBacktracking, System.Buffers.ArrayPool<T>, and MemoryExtensions.ToLowerInvariant all require .NET 8+. Use EricksonLopez.DomainPrimitives.Abstractions for shared contracts in netstandard2.0 projects.
Primary development target: net10.0 LTS. All benchmarks and new feature development target NET 10. See ADR-016 for the full rationale.

Native AOT

All generated code is AOT-compatible:

  • Zero reflection in hot paths (Type.GetMethod(), Activator, Expression<> are never used)
  • SmartEnum.GetAll() is a static readonly array β€” no runtime reflection
  • IsAotCompatible=true in project metadata
  • CI gate: dotnet publish with Native AOT verifies compatibility on every commit

Generated BCL Interfaces

For every domain primitive, the generator emits:

Interface String Numeric Date StrongId ValueObject
IParsable<T> βœ… βœ… βœ… βœ… πŸ”œ v2.0
ISpanParsable<T> βœ… βœ… βœ… βœ… πŸ”œ v2.0
IUtf8SpanParsable<T> (NET8+) βœ… βœ… βœ… βœ… πŸ”œ v2.0
IFormattable βœ… βœ… βœ… βœ… βœ…
ISpanFormattable βœ… βœ… βœ… βœ… βœ…
IUtf8SpanFormattable (NET8+) βœ… βœ… βœ… βœ… πŸ”œ v2.0
IComparable<T> βœ… βœ… βœ… βœ… N/A*
IEqualityOperators<T,T,bool> βœ… βœ… βœ… βœ… βœ…

*IComparable<T> on ValueObject is intentionally not generated β€” composite types have no canonical ordering unless the domain explicitly defines one.

πŸ“š Documentation

  • πŸ›‘οΈ Security Gates β€” SEC-001 through SEC-006 explained
  • πŸ—ΊοΈ Feature Gaps β€” What's missing and what we explicitly reject
  • 🍳 Cookbook β€” Common problems solved with DomainPrimitives
  • πŸ“– API Reference β€” Interfaces, exceptions, and factory methods
  • πŸ“¦ Packages β€” All 15 NuGet packages, TFM matrix, and dependency graph
  • πŸ”„ Migration from Vogen β€” Step-by-step migration guide
  • πŸ”„ Migration from StronglyTypedId β€” Step-by-step migration guide
  • πŸ“Š Benchmark Results β€” Performance data and allocation audit
  • πŸ“Š Benchmark Plan β€” 16 BenchmarkDotNet scenarios
  • πŸ—οΈ System Overview β€” Architecture and project dependency diagram
  • βš™οΈ CI/CD Pipelines β€” Build, test, quality gates, and supply chain security
  • πŸ—ΊοΈ Roadmap β€” NOW / NEXT / LATER horizon planning
  • πŸ“ Changelog β€” All notable changes per version

πŸš€ Sample Projects

The samples/OfficialSample/ folder contains step-by-step integration examples. Numbers are non-consecutive by design β€” they match the chapter index of the full documentation and leave room for future samples to be inserted without renumbering.

# Sample Description
1 1-GettingStarted Core concepts and Quick Start
4 4-ValueObjects Structural validation and built-in primitive catalog
5 5-StronglyTypedIds Eliminating Primitive Obsession
15 15-AspNetCoreIntegration ASP.NET Core, HTTP validation and JSON
16 16-EFCoreIntegration Domain persistence with EF Core
17 17-MediatRIntegration Advanced pipeline behavior integration
20 20-EndToEndApplication Enterprise architecture (scalability, observability, error handling)
23 23-DapperIntegration Dapper TypeHandlers with auto-discovery (DapperDomainPrimitivesRegistration.RegisterAll())
24 24-OpenApiIntegration Swagger/OpenAPI schema filters β€” domain primitives shown as correct JSON types

🀝 Contributing & Community

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

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
3.0.0 67 9/20/2026
2.0.0 114 8/25/2026
1.0.0 99 8/11/2026