Skip to main content
net10.0net11.0.NETStandard2.0

Source Generator to generate strongly-typed id with all needed helpers such as converters for System.Text.Json, Newtonsoft.Json, or MongoDB.

Published
Sep 9, 2026
1mo ago
Total Downloads
196.0K
196,034

Install

dotnet add package Meziantou.Framework.StronglyTypedId --version 3.0.6
ProductVersions
.NET
net10.0net11.0
.NET Standard
netstandard2.0

README

Meziantou.Framework.StronglyTypedId

The source generator generates constructors, properties, equality members, common interfaces, converters for multiple serializers

[StronglyTypedId<int>]
public partial struct ProjectId { }

or using typeof if you're not using a version of C# that supports generic attributes

[StronglyTypedId(typeof(int))]
public partial struct ProjectId { }

StronglyTypedId generates the following code:

[System.ComponentModel.TypeConverterAttribute(typeof(ProjectIdTypeConverter))]
[System.Text.Json.Serialization.JsonConverterAttribute(typeof(ProjectIdJsonConverter))]
[Newtonsoft.Json.JsonConverterAttribute(typeof(ProjectIdNewtonsoftJsonConverter))]
[MongoDB.Bson.Serialization.Attributes.BsonSerializerAttribute(typeof(ProjectIdBsonConverter))]
public partial struct ProjectId :
    System.IEquatable<ProjectId>,
    System.IParsable<ProjectId>,        // .NET 7+
    System.ISpanParsable<ProjectId>,    // .NET 7+
    IStronglyTypedId,                   // When Meziantou.Framework.StronglyTypedId.Interfaces is referenced
    IStronglyTypedId<int>,              // When Meziantou.Framework.StronglyTypedId.Interfaces is referenced
    IComparable, IComparable<ProjectId> // When at least one of the interface is explicitly defined by the user
{
    public int Value { get; }
    public string ValueAsString { get; } // Value formatted using InvariantCulture

    private ProjectId(int value);

    public static ProjectId FromInt32(int value);
    public static ProjectId Parse(string value);
    public static ProjectId Parse(ReadOnlySpan<char> value);
    public static bool TryParse(string value, out ProjectId result);
    public static bool TryParse(ReadOnlySpan<char> value, out ProjectId result);
    public override int GetHashCode();
    public override bool Equals(object? other);
    public bool Equals(ProjectId other);
    public static bool operator ==(ProjectId a, ProjectId b);
    public static bool operator !=(ProjectId a, ProjectId b);
    public override string ToString();

    private partial class ProjectIdTypeConverter : System.ComponentModel.TypeConverter
    {
        public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType);
        public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value);
        public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType);
        public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType);
    }

    // Generated only when System.Text.Json is accessible
    private partial class ProjectIdJsonConverter : System.Text.Json.Serialization.JsonConverter<ProjectId>
    {
        public override void Write(System.Text.Json.Utf8JsonWriter writer, ProjectId value, System.Text.Json.JsonSerializerOptions options);
        public override ProjectId Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options);
    }

    // Generated only when Newtonsoft.Json is accessible
    private partial class ProjectIdNewtonsoftJsonConverter : Newtonsoft.Json.JsonConverter
    {
        public override bool CanRead { get; }
        public override bool CanWrite { get; }
        public override bool CanConvert(System.Type type);
        public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object? value, Newtonsoft.Json.JsonSerializer serializer);
        public override object ReadJson(Newtonsoft.Json.JsonReader reader, System.Type objectType, object? existingValue, Newtonsoft.Json.JsonSerializer serializer);
    }

    // Generated only when MongoDB.Bson.Serialization.Serializers.SerializerBase is accessible
    private partial class ProjectIdBsonConverter : MongoDB.Bson.Serialization.Serializers.SerializerBase<ProjectId>
    {
        public override ProjectId Deserialize(MongoDB.Bson.Serialization.BsonDeserializationContext context, MongoDB.Bson.Serialization.BsonDeserializationArgs args);
        public override void Serialize(MongoDB.Bson.Serialization.BsonSerializationContext context, MongoDB.Bson.Serialization.BsonSerializationArgs args, ProjectId value);
    }
}

If the Meziantou.Framework.StronglyTypedId.Interfaces NuGet package is present, the generator will implements IStronglyTypedId and IStronglyTypedId.

Supported types

  • System.Boolean
  • System.Byte
  • System.DateTime
  • System.DateTimeOffset
  • System.Decimal
  • System.Double
  • System.Guid
  • System.Half
  • System.Int16
  • System.Int32
  • System.Int64
  • System.Int128
  • System.Numerics.BigInteger
  • System.SByte
  • System.Single
  • System.String
  • System.Uint16
  • System.Uint32
  • System.Uint64
  • System.Uint128
  • MongoDB.Bson.ObjectId

Configuration

You can configure the code generation using the [StronglyTypedIdAttribute] attribute:

[StronglyTypedId(idType: typeof(long),
                 generateSystemTextJsonConverter: true,
                 generateNewtonsoftJsonConverter: true,
                 generateSystemComponentModelTypeConverter: true,
                 generateMongoDBBsonSerialization: true,
                 addCodeGeneratedAttribute: true
                 )]
public partial struct ProjectId { }

When the underlying type is System.Guid, you can choose how the generated New() method creates the value. The default is Version4 (Guid.NewGuid()). Version7 (Guid.CreateVersion7()) requires .NET 9 or later, otherwise the MFSTID0002 diagnostic is reported. None doesn't generate the New() method. The option only applies to System.Guid; using it with another underlying type reports the MFSTID0003 diagnostic:

[StronglyTypedId<Guid>(GuidGenerationStrategy = GuidGenerationStrategy.Version7)]
public partial struct ProjectId { }

// Generated by the source generator
public partial struct ProjectId
{
    public static ProjectId New() => new ProjectId(Guid.CreateVersion7());
}
[StronglyTypedId<Guid>(GuidGenerationStrategy = GuidGenerationStrategy.None)]
public partial struct ProjectId { } // No New() method is generated

You can configure the default value of the options for all the types of an assembly using the [assembly: StronglyTypedIdDefaults] attribute. Options set on [StronglyTypedId] take precedence over the assembly-level defaults:

[assembly: StronglyTypedIdDefaults(GuidGenerationStrategy = GuidGenerationStrategy.Version7,
                                   GenerateNewtonsoftJsonConverter = false,
                                   GenerateMongoDBBsonSerialization = false,
                                   GenerateSystemComponentModelTypeConverter = false,
                                   GenerateSystemTextJsonConverter = true,
                                   AddCodeGeneratedAttribute = true,
                                   StringComparison = StringComparison.Ordinal,
                                   GenerateToStringAsRecord = true)]

[StronglyTypedId<Guid>]
public partial struct ProjectId { } // New() uses Guid.CreateVersion7()

[StronglyTypedId<Guid>(GuidGenerationStrategy = GuidGenerationStrategy.Version4)]
public partial struct CustomerId { } // New() uses Guid.NewGuid()

You can generate IComparable, IComparable and comparison operators by adding one interface:

[StronglyTypedId<int>]
public partial struct ProjectId : IComparable { }

// Generated by the source generator
public partial struct ProjectId : IComparable<ProjectId>
{
	public int CompareTo(object? other);
	public int CompareTo(ProjectId other);
	public static bool operator <(ProjectId left, ProjectId right);
	public static bool operator <=(ProjectId left, ProjectId right);
	public static bool operator >(ProjectId left, ProjectId right);
	public static bool operator >=(ProjectId left, ProjectId right);
}

Additional resources

Analyzer rules

Id Category Description Severity Enabled
MFSTID0001 StronglyTypedId Not supported type Error ✔️
MFSTID0002 StronglyTypedId Guid generation strategy is not supported by the target framework Error ✔️
MFSTID0003 StronglyTypedId Guid generation strategy is only applicable to Guid Warning ✔️
MFSTID0004 StronglyTypedId Generic types are not supported Error ✔️

Changes from Previous Version

No structural changes between v3.0.5 and v3.0.6.

Adoption Assessment

A verified publisher signal is available. Assess fit from the package's supported frameworks, license and dependency graph rather than popularity alone.

Compatibility metadata lists 3 target frameworks; confirm the exact target used by your application.

Related Guides & Workflows

Learn how to evaluate, integrate, or audit this package in your .NET project.