C# strongly typed IDs

By Global Code Factory

Updated on:

C# Strongly Typed IDs enhances the expressiveness and type safety of your program. They stops misuse of primitive IDs like Guid, int, or string across domain models. Your code becomes more reliable as a result.

What Are Strongly Typed IDs in C#?

Entity IDs are frequently represents primitive values like Guid, int, or string. But because these primitives are interchangeable, can lead to bugs, particularly when several ID types share an underlying type.

Instead of:

void ProcessOrder(Guid orderId, Guid userId) { ... }

We define:

public record OrderId(Guid Value);
public record UserId(Guid Value);

This method ensures that every ID is typed strongly and avoids confusion during compilation.

Why Use Strongly Typed IDs in C#?

Improve Type Safety: Swapped IDs are catches as errors by the compiler.

Improve Readability — Code becomes self-documenting with explicit types.

Encapsulate Behavior: Include formatting logic or validation within the ID type.

Additionally, Domain-Driven Design (DDD) works well with strongly typed IDs. They make models more intention-revealing while solving primitive obsession.

Implementation Examples of C# strongly typed IDs

Manual Struct or Record Approach
Struct (mutable-safe, value-type):

public readonly struct OrderId : IEquatable<OrderId>
{
    public Guid Value { get; }
    public OrderId(Guid value) => Value = value;
    public override string ToString() => Value.ToString();
    public bool Equals(OrderId other) => Value == other.Value;
    public override int GetHashCode() => Value.GetHashCode();
    public static implicit operator Guid(OrderId id) => id.Value;
}

Record (compact, immutable):

public record ProductId(int Value);

Use Source Generators for Boilerplate-Free IDs

You can use custom C# source generators or tools like StronglyTypedId to avoid writing redundant code:

[StronglyTypedId]
public partial struct FooId { }

This generates type-safe ID structs with comparability, parsing, JSON conversion, and more.

A Brief Overview of Benefits

  • Use compile-time safety to prevent ID misuse.
  • APIs that are clean, such as GetOrder(OrderId id) rather than GetOrder(Guid id).
  • Boilerplate-free with source generators.
  • Functions flawlessly with EF Core, ASP.NET Core, and serialization.

Strongly Typed IDs are a potent technique to increase your applications’ safety and readability. You can avoid inadvertent usage and communicate your intention to other developers by substituting specified, domain-specific types for generic int or Guid identifiers. This method minimizes errors, improves maintainability, and is entirely consistent with domain-driven design and clean code. Furthermore, strongly typed IDs enhance the self-documenting nature of your code and make refactoring safer, both of which are essential for long-term project success in .NET applications.

Leave a Comment