Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
S

StronglyTypedId

> 编程语言
Open source

A Rosyln-powered generator for strongly-typed IDs

1.7K stars0 likes0 views
WebsiteGitHub

About

A Rosyln-powered generator for strongly-typed IDs

StronglyTypedId

StronglyTypedId makes creating strongly-typed IDs as easy as adding an attribute! No more accidentally passing arguments in the wrong order to methods - StronglyTypedId uses .NET 7+'s compile-time incremental source generators to generate the boilerplate required to use strongly-typed IDs.

Simply, install the required package add the [StronglyTypedId] attribute to a struct (in the StronglyTypedIds namespace):

using StronglyTypedIds;
 
[StronglyTypedId] // <- Add this attribute to auto-generate the rest of the type
public partial struct FooId { }

and the source generator magically generates the backing code when you save the file! Use Go to Definition to see the generated code:

StronglyTypedId requires the .NET Core SDK v7.0.100 or greater.

Why do I need this library?

I have written a blog-post series on strongly-typed IDs that explains the issues and rational behind this library. For a detailed view, I suggest starting there, but I provide a brief introduction here.

This library is designed to tackle a specific instance of primitive obsession, whereby we use primitive objects (Guid/string/int/long etc) to represent the IDs of domain objects. The problem is that these IDs are all interchangeable - an order ID can be assigned to a product ID, despite the fact that is likely nonsensical from the domain point of view. See here for a more concrete example.

By using strongly-typed IDs, we give each ID its own Type which wraps the underlying primitive value. This ensures you can only use the ID where it makes sense: ProductIds can only be assigned to products, or you can only search for products using a ProductId, not an OrderId.

Unfortunately, taking this approach requires a lot of boilerplate and ceremony to make working with the IDs manageable. This library abstracts all that away from you, by generating the boilerplate at build-time by using a Roslyn-powered code generator.

Requirements

The StronglyTypedId NuGet package is a .NET Standard 2.0 package.

You must be using the .NET 7+ SDK (though you can compile for other target frameworks like .NET Core 2.1 and .NET Framework 4.8)

Installing

To use StronglyTypedIds, install the StronglyTypedId NuGet package into your csproj file, for example by running

dotnet add package StronglyTypedId --version 1.0.0-beta08

This adds a <PackageReference> to your project. You can additionally mark the package as PrivateAssets="all" and ExcludeAssets="runtime".

Setting PrivateAssets="all" means any projects referencing this one will not also get a reference to the StronglyTypedId package. Setting ExcludeAssets="runtime" ensures the StronglyTypedId.Attributes.dll file is not copied to your build output (it is not required at runtime).

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
  
  <ItemGroup>
    
    <PackageReference Include="StronglyTypedId" Version="1.0.0-beta08" PrivateAssets="all" ExcludeAssets="runtime" />
    
  </ItemGroup>

</Project>

Usage

To create a strongly-typed ID, create a partial struct with the desired name, and decorate it with the [StronglyTypedId] attribute, in the StronglyTypedIds namespace:

using StronglyTypedIds;

[StronglyTypedId] // Add this attribute to auto-generate the rest of the type
public partial struct FooId { }

This generates the "default" strongly-typed ID using a Guid backing field. You can use your IDE's Go to Definition functionality on your ID to see the_exact code generated by the source generator. The ID implements the following interfaces automatically:

  • IComparable<T>
  • IEquatable<T>
  • IFormattable
  • ISpanFormattable (.NET 6+)
  • IParsable<T> (.NET 7+)
  • ISpanParsable<T> (.NET 7+)
  • IUtf8SpanFormattable (.NET 8+)
  • IUtf8SpanParsable<T> (.NET 8+)

And it additionally includes two converters/serializers:

  • System.ComponentModel.TypeConverter
  • System.Text.Json.Serialization.JsonConverter

This provides basic integration for many use cases, but you may want to customize the IDs further, as you'll see shortly.

Using different types as a backing fields

The default strongly-typed ID uses a Guid backing field:

using StronglyTypedIds;

[StronglyTypedId]
public partial struct FooId { }

var id = new FooId(Guid.NewGuid());

You can choose a different type backing field, by passing a value of the Template enum in the constructor.

using StronglyTypedIds;

[StronglyTypedId(Template.Int)]
public partial struct FooId { }

var id = new FooId(123);

Currently supported built-in backing types are:

  • Guid (the default)
  • int
  • long
  • string

Changing the defaults globally

If you wish to change the template used by default for all the [StronglyTypedId]-decorated IDs in your project, you can use the assembly attribute [StronglyTypedIdDefaults] to set all of these. For example, the following changes the default backing-type for all IDs to int

// Set the defaults for the project
[assembly:StronglyTypedIdDefaults(Template.Int)]

[StronglyTypedId] // Uses the default 'int' template
public partial struct OrderId { }

[StronglyTypedId] // Uses the default 'int' template
public partial struct UserId { } 

[StronglyTypedId(Template.Guid)] // Overrides the default to use 'Guid' template
public partial struct HostId { } 

Using custom templates

In addition to the built-in templates, you can provide your own templates for use with strongly typed IDs. To do this, do the following:

  • Add a file to your project with the name TEMPLATE.typedid, where TEMPLATE is the name of the template
  • Update the template with your desired ID content. Use PLACEHOLDERID inside the template. This will be replaced with the ID's name when generating the template.
  • Update the "build action" for the template to AdditionalFiles or C# analyzer additional file (depending on your IDE).

For example, you could create a template that provides an EF Core ValueConverter implementation called guid-efcore.typedid like this:

partial struct PLACEHOLDERID
{
    public class EfCoreValueConverter : global::Microsoft.EntityFrameworkCore.Storage.ValueConversion.ValueConverter<PLACEHOLDERID, global::System.Guid>
    {
        public EfCoreValueConverter() : this(null) { }
        public EfCoreValueConverter(global::Microsoft.EntityFrameworkCore.Storage.ValueConversion.ConverterMappingHints? mappingHints = null)
            : base(
                id => id.Value,
                value => new PLACEHOLDERID(value),
                mappingHints
            ) { }
    }
}

Note that the content of the guid-efcore.typedid file is valid C#. One easy way to author these templates is to create a .cs file containing the code you want for your ID, then rename your ID to PLACEHOLDERID, change the file extension from .cs to _.typedid, and then set the build action.

After creating a template in your project you can apply it to your IDs like this:

// Use the built-in Guid template and also the custom template
[StronglyTypedId(Template.Guid, "guid-efcore")] 
public partial struct GuidId {}

This shows another important feature: you can specify multiple templates to use when generating the ID.

Using multiple templates

When specifying the templates for an ID, you can specify

  • 0 or 1 built-in templates (using Template.Guid etc)
  • 0 or more custom templates

For example:

…

Similarly, for the optional [StronglyTypedIdDefaults] assembly attribute, which defines the default templates to use when you use the raw [StronglyTypedId] attribute, you use a combination of built-in and/or custom templates:

…

To simplify the creation of templates, the StronglyTypedId package includes a code-fix provider to generate a template.

Creating a custom template with the Roslyn CodeFix provider

As well as the source generator, the StronglyTypedId NuGet package includes a CodeFix provider that looks for cases where you have specified a custom template that the source generator cannot find. For example, in the following code,the "some-int" template does not yet exist:

[StronglyTypedId("some-int")] // does not exist
public partial struct MyStruct { }

In the IDE, you can see the generator has marked this as an error:

The image above also shows that there's a CodeFix action available. Clicking the action reveals the possible fix: Add some-int.typedid template to the project, and shows a preview of the file that will be added:

Choosing this option will add the template to your project.

Unfortunately, due to limitations with the Roslyn APIs, it's not possible to add the new template with the required AdditionalFiles/C# analyzer additional file build action already set. Until you change the build-action, the error will remain on your [StronglyTypedId] attribute.

Right-click the newly-added template, choose Properties, and change the Build Action to either C# analyzer additional file (Visual Studio 2022) or AdditionalFiles (JetBrains Rider). The source generator will then detect your template and the error will disappear.

The CodeFix provider does a basic check against the name of the template you're trying to create. If it includes int, long, or string, the template it creates will be based on one of those backing types. Otherwise, the template is based on a Guid backing type.

Once the template is created, you're free to edit it as required.

"Community" templates package StronglyTypedId.Templates

The "template-based" design of StronglyTypedId is intended to make it easy to get started, while also giving you the flexibility to customise your IDs to your needs.

To make it easier to share templates with multiple people, and optional StronglyTypedId.Templates NuGet package is available that includes various converters and other backing types. To use these templates, add the StronglyTypedId.Templates package to your project:

dotnet add package StronglyTypedId.Templates --version 1.0.0-beta08

You will then be able to reference any of the templates it includes. This includes "complete" implementations, including multiple converters, for various backing types:

  • guid-full
  • int-full
  • long-full
  • string-full
  • nullablestring-full
  • newid-full

It also includes "standalone" EF Core, Dapper, and Newtonsoft JSON converter templates to enhance the Guid/int/long/string built-in templates. For example

  • Templates for use with Template.Guid
    • guid-dapper
    • guid-efcore
    • guid-newtonsoftjson
  • Templates for use with Template.Int
    • int-dapper
    • int-efcore
    • int-newtonsoftjson
  • Templates for use with Template.Long
    • long-dapper
    • long-efcore
    • long-newtonsoftjson
  • Templates for use with Template.String
    • string-dapper
    • string-efcore
    • string-newtonsoftjson

For the full list o

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

C#

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言