Skip to main content

StronglyTypedUid by Victor Sánchez

Nuget / site data

Nuget GitHub last commit GitHub Repo stars

Details

Info

info

Name: StronglyTypedUid

Implementation of Strongly Typed Ids.

Author: Victor Sánchez

NuGet: https://www.nuget.org/packages/StronglyTypedUid/

You can find more details at https://github.com/vicosanz/StronglyTypedUid

Source : https://github.com/vicosanz/StronglyTypedUid

Original Readme

note

StronglyTypedUid

C# Implementation of Strongly Typed Id made easy.

StronglyTypedUid NuGet Badge

StronglyTypedUid.Generator NuGet Badge

publish to nuget

Buy me a coffee

If you want to reward my effort, ☕ https://www.paypal.com/paypalme/vicosanzdev?locale.x=es_XC

All strongly typed ids are source generated, you must create a record struct in this ways:

Using attribute decorating a record struct (default Guid version)

    [StronglyTypedUid] 
public readonly partial record struct CustomerId { }

If you want change to Ulid

    [StronglyTypedUid(asUlid:true)] 
public readonly partial record struct CustomerId { }

Create additional converters to popular packages like efcore, dapper and newtonsoftjson

    [StronglyTypedUid(asUlid:true, [EnumAdditionalConverters.EFCore, EnumAdditionalConverters.Dapper, EnumAdditionalConverters.NewtonsoftJson])]
public readonly partial record struct CustomerId { }

The generator will create a partial record struct of the same name

// Auto generated code
[TypeConverter(typeof(CustomerIdTypeConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(CustomerIdJsonConverter))]
public readonly partial record struct CustomerId(Guid Value) : IStronglyTypedGuid
{
public static CustomerId Empty => new(Guid.Empty);

public static CustomerId NewCustomerId() => new(Guid.NewGuid());

public static implicit operator CustomerId(Guid value) => new(value);

public static explicit operator Guid(CustomerId value) => value.Value;

public bool IsEmpty => Value == Guid.Empty;

public override string ToString() => Value.ToString();

public static CustomerId Parse(string text) => new CustomerId(Guid.Parse(text));

public static bool TryParse(string text, out CustomerId result)
{
try
{
if (Guid.TryParse(text, out Guid uid))
{
result = uid;
return true;
}
}
catch (Exception)
{
}
result = default;
return false;
}
}

You can add additional logic to your strongly type id.

    [StronglyTypedUid] 
public readonly partial record struct CustomerId
{
public override string ToTaggedString() => $"CID-{Value}";

public static bool TryParseTagged(string text, out CustomerId customer)
{
try
{
if (Guid.TryParse(text[4..], out Guid result))
{
customer = result;
return true;
}
}
catch (Exception)
{
}
customer = default;
return false;
}
}

The new type is decorated with a TypeConverter and a JsonConverter automatically

[TypeConverter(typeof(CustomerIdTypeConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(CustomerIdJsonConverter))]

You can serialize and deserialize without problems

public record Customer(CustomerId Id, string Name);


var newcustomer = new Customer(CustomerId.NewCustomerId(), "Jhon");

var serializeOptions = new JsonSerializerOptions
{
WriteIndented = true
};
var json = JsonSerializer.Serialize(newcustomer, serializeOptions);

var newcustomer2 = JsonSerializer.Deserialize<Customer>(json);

About

note

Transforming a record into a GUID

How to use

Example ( source csproj, source files )

This is the CSharp Project that references StronglyTypedUid

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

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="StronglyTypedUid" Version="1.0.1" />
<PackageReference Include="StronglyTypedUid.Common" Version="1.0.1" />
<PackageReference Include="StronglyTypedUid.Generator" Version="1.0.1" />
</ItemGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
</Project>

Generated Files

Those are taken from $(BaseIntermediateOutputPath)\GX

using System;
using System.ComponentModel;
using System.Globalization;
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Buffers;

using StronglyTypedUid;

#nullable enable

namespace RecordToGuid;

[TypeConverter(typeof(PersonIdTypeConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(PersonIdJsonConverter))]
public readonly partial record struct PersonId(Guid Value) : IStronglyTypedUid
{
public static PersonId Empty => new(Guid.Empty);

public static PersonId NewPersonId() => new(Guid.NewGuid());

public static implicit operator PersonId(Guid value) => new(value);

public static explicit operator Guid(PersonId value) => value.Value;

public bool IsEmpty => Value == Guid.Empty;

public override string ToString() => Value.ToString();

public static PersonId Parse(string text) => new PersonId(Guid.Parse(text));

public static bool TryParse(string text, out PersonId result)
{
try
{
if (Guid.TryParse(text, out Guid uid))
{
result = uid;
return true;
}
}
catch (Exception)
{
}
result = default;
return false;
}
}

public class PersonIdTypeConverter : TypeConverter
{
private static readonly Type StringType = typeof(string);
private static readonly Type UidType = typeof(Guid);

public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) =>
sourceType == StringType || sourceType == UidType || base.CanConvertFrom(context, sourceType);

public override object? ConvertFrom(ITypeDescriptorContext? context,
CultureInfo? culture, object value) => value switch
{
Guid g => new PersonId(g),
string stringValue => PersonId.Parse(stringValue),
_ => base.ConvertFrom(context, culture, value),
};

public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) =>
destinationType == StringType || destinationType == UidType || base.CanConvertTo(context, destinationType);

public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
{
if (value is PersonId result)
{
if (destinationType == StringType)
{
return result.ToString();
}
if (destinationType == UidType)
{
return (Guid)result;
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
}

public class PersonIdJsonConverter : JsonConverter<PersonId>
{
public override PersonId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
try
{
if (reader.TokenType != JsonTokenType.String) throw new JsonException("Expected string");
return new PersonId(new Guid(reader.GetString()));
}
catch (IndexOutOfRangeException e)
{
throw new JsonException("PersonId invalid: length must be 36", e);
}
catch (OverflowException e)
{
throw new JsonException("PersonId invalid: invalid character", e);
}
}
public override void Write(Utf8JsonWriter writer, PersonId value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}


Usefull

Download Example (.NET C# )

Share StronglyTypedUid

https://ignatandrei.github.io/RSCG_Examples/v2/docs/StronglyTypedUid

In the same category (PrimitiveObsession) - 4 other generators

DomainPrimitives

Strongly

UnitGenerator

Vogen