Skip to main content
Version: 0.2.x (0.2.1)

Compared with other libraries

AdCodicem.ValueObjects is not the first answer to primitive obsession in .NET. Three established libraries generate value objects too, and each of them is the better choice for some projects. This page is meant to help you tell which one yours is.

The comparison was made in September 2026 against Vogen 8.0.7, StronglyTypedId 1.0.0-beta08 — the version most of its users install, although nuget.org still lists 0.2.1 as the latest stable — and Thinktecture.Runtime.Extensions 10.5.0, from their source, their documentation and, where the documentation was unclear, a compiled test project. Libraries move: if something here is out of date, open an issue and it will be corrected.

At a glance​

AdCodicem.ValueObjectsVogenStronglyTypedIdThinktecture
Consumer frameworks.NET 10netstandard2.0 and laterolder frameworks too, including .NET Framework, with a .NET 7+ SDK.NET 8 and later
Type kindsreadonly structclass, struct, recordstructclass, struct
Underlying types22 built-in typesany type but a collectionGuid, int, long, string; others through templatesany type
Length, pattern and range declared on the attributeyesnonono
Validation and normalization hooksinterfaces, checked by the compilermethods found by namenonea partial method with ref parameters
Rejection carries a stable error codeyesno, a message—no, a message (custom error types possible)
default and new T() rejected at build timeyesyesnoyes, for structs
String comparisonordinal, or declared per typeordinal, configurableordinalcase-insensitive by default, configurable
System.Text.Jsonyesyesyesyes
Source-generated JsonSerializerContextdeclared on the context, at compile timea factory passed in the options at run timeconverters passed in the optionsnot documented
EF Core mappingevery value object of an assembly in one callper property, or a marker class listing the typesper typeevery value object in one call
Column size from the type's rulesyesnonono, a max-length strategy can be configured
EF Core reads validateon request (strict: true)by default—no, reads use the constructor
ASP.NET Core model bindingyesthrough the TypeConverterthrough the TypeConverteryes
Problem details carry the violated rule's codeyesnonono
OpenAPIbuilt-in stack, with lengths, pattern, bounds, enumtype and format; Swashbuckle or built-in stacknoneSwashbuckle, type of the key
FluentValidationyesthird-party packagenono
Dapperyesyesthrough a templateno
Other serializers and storesNewtonsoft.JsonNewtonsoft.Json, LinqToDB, ServiceStack.Text, Orleans, MessagePack, BSON, XMLNewtonsoft.JsonNewtonsoft.Json, MessagePack
Contract test kit for your own typesyesnonono
Prefixed public identifiers (acc_…)yesnonono
Beyond single valuesnononocomplex value objects, smart enums, discriminated unions
LicenceMITApache-2.0MITBSD-3-Clause

Where this library differs​

A rule is declared once and reaches every boundary. MaxLength = 34 validates, sizes the EF Core column and becomes the OpenAPI maxLength; Pattern and Minimum do the same for the schema; known values become the enum. In the other three, a length or a pattern is code inside a validation method, so the column and the schema have to be told separately — which is exactly the drift that primitive obsession produces.

A rejection is data a client can act on. Validation returns a ValidationResult struct holding a stable code and a message, and allocates nothing when the value is valid. The code travels to problem details responses and FluentValidation failures, so an API client branches on value_object.too_long rather than on English.

Rules are found by interface, not by name. IValueObjectValidator<T> and IValueObjectNormalizer<T> let the compiler check each signature, and VO0011 reports the one mistake left: a rule written without its interface, which would otherwise never run.

Source-generated JSON is declared, not remembered. Naming ValueObjectJsonConverterFactory in [JsonSourceGenerationOptions] works at compile time, on the context itself. With Vogen and StronglyTypedId the converters have to be added to the options at run time instead, and forgetting them can fail quietly: in a test against Vogen 8.0.7, the value object was written as {} and read back uninitialized.

The integrations go further than serialization. Problem details, FluentValidation rules that defer to the type, an OpenAPI schema carrying the constraints, a contract kit that tests your own types, and Stripe-style public identifiers are part of the library rather than left to you.

Where the others are stronger​

They run in more places. This library requires .NET 10. Vogen targets netstandard2.0, StronglyTypedId runs on older frameworks including .NET Framework, and Thinktecture supports .NET 8. For an application that cannot move to .NET 10, the choice is made.

They accept more shapes. Vogen wraps any type but a collection, as a class, a struct or a record; Thinktecture accepts any key, generic ones included. This library is deliberately limited to 22 underlying types and to readonly struct: Design decisions explains why, but if you need a class or a Uri, it is not for you.

They cover more stores and serializers. Vogen generates support for LinqToDB, ServiceStack.Text, Orleans, MessagePack, MongoDB's BSON and XML; Thinktecture for MessagePack. Both work with Swashbuckle, which this library does not support.

Thinktecture goes beyond single values. Complex value objects with several members, smart enums and discriminated unions are out of scope here.

Vogen validates what it reads from the database by default. This library skips validation on the EF Core read path unless asked, a performance choice that assumes the application owns its tables. For a database several systems write to, Vogen's default is the safer one; here, strict: true gives the same behaviour.

They are proven. Vogen and StronglyTypedId have millions of downloads and years of production use behind them. This library was first published in September 2026 and is still at 0.x.

Choosing​

  • On .NET 10, with an API and a database, where a rule should be written once and reach the schema and the column: this library is built for that.
  • On an older framework, or with a store this library does not cover: Vogen.
  • Only strongly-typed identifiers, and nothing to validate: StronglyTypedId does that with the least ceremony.
  • Smart enums, discriminated unions or multi-member value objects alongside single values: Thinktecture.

Moving from one of them? Migrate from another library maps each one's surface onto this one.