RSCG – Strongly
Generate and customize strong id structs
This is how you can use Strongly .
The code that you start with is
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Strongly" Version="1.1.0" OutputItemType="Analyzer" />
</ItemGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
</Project>
The code that you will use is
// See https://aka.ms/new-console-template for more information
using StronglyDemo;
Person p = new();
//p.SetBirthDate(1970, 4, 16);
p.SetBirthDate(new YearId(1970) , new MonthId(4),new DayId( 16));
Console.WriteLine(p.BirthDate);
using Strongly;
namespace StronglyDemo;
[Strongly(backingType: StronglyType.Int)]
public partial struct YearId { }
[Strongly(backingType: StronglyType.Int)]
public partial struct MonthId { }
[Strongly(backingType: StronglyType.Int)]
public partial struct DayId { }
internal class Person
{
public DateTime BirthDate { get; internal set; }
public void SetBirthDate(YearId yearId,MonthId monthId,DayId dayId)
{
BirthDate = new DateTime(yearId.Value, monthId.Value, dayId.Value);
}
}
The code that is generated is
#if STRONGLY_TYPED_EMBED_ATTRIBUTES
using System;
namespace Strongly
{
/// <summary>
/// Place on partial structs to make the type a strongly-typed ID
/// </summary>
[AttributeUsage(AttributeTargets.Struct)]
[System.Diagnostics.Conditional("STRONGLY_TYPED_USAGES")]
internal sealed class StronglyAttribute : Attribute
{
/// <summary>
/// Make the struct a strongly typed ID
/// </summary>
/// <param name="backingType">The <see cref="Type"/> to use to store the strongly-typed ID value.
/// If not set, uses <see cref="StronglyDefaultsAttribute.BackingType"/>, which defaults to <see cref="StronglyType.Guid"/></param>
/// <param name="converters">Converters to create for serializing/deserializing the strongly-typed ID value.
/// If not set, uses <see cref="StronglyDefaultsAttribute.Converters"/>, which defaults to <see cref="StronglyConverter.NewtonsoftJson"/>
/// and <see cref="StronglyConverter.TypeConverter"/></param>
/// <param name="implementations">Interfaces and patterns the strongly typed id should implement
/// If not set, uses <see cref="StronglyDefaultsAttribute.Implementations"/>, which defaults to <see cref="StronglyImplementations.IEquatable"/>
/// and <see cref="StronglyImplementations.IComparable"/></param>
public StronglyAttribute(
StronglyType backingType = StronglyType.Default,
StronglyConverter converters = StronglyConverter.Default,
StronglyImplementations implementations = StronglyImplementations.Default)
{
BackingType = backingType;
Converters = converters;
Implementations = implementations;
}
/// <summary>
/// The <see cref="Type"/> to use to store the strongly-typed ID value
/// </summary>
public StronglyType BackingType { get; }
/// <summary>
/// JSON library used to serialize/deserialize strongly-typed ID value
/// </summary>
public StronglyConverter Converters { get; }
/// <summary>
/// Interfaces and patterns the strongly typed id should implement
/// </summary>
public StronglyImplementations Implementations { get; }
}
}
#endif
#if STRONGLY_TYPED_EMBED_ATTRIBUTES
using System;
namespace Strongly
{
/// <summary>
/// Converters used to to serialize/deserialize strongly-typed ID values
/// </summary>
[Flags]
internal enum StronglyConverter
{
// Used with HasFlag, so needs to be 1, 2, 4 etc
/// <summary>
/// Don't create any converters for the strongly typed ID
/// </summary>
None = 0,
/// <summary>
/// Use the default converters for the strongly typed Id.
/// This will be the value provided in the <see cref="StronglyDefaultsAttribute"/>, which falls back to
/// <see cref="TypeConverter"/> and <see cref="SystemTextJson"/>
/// </summary>
Default = 1,
/// <summary>
/// Creates a <see cref="TypeConverter"/> for converting from the strongly typed ID to and from a string
/// </summary>
TypeConverter = 2,
/// <summary>
/// Creates a Newtonsoft.Json.JsonConverter for serializing the strongly typed id to its primitive value
/// </summary>
NewtonsoftJson = 4,
/// <summary>
/// Creates a System.Text.Json.Serialization.JsonConverter for serializing the strongly typed id to its primitive value
/// </summary>
SystemTextJson = 8,
/// <summary>
/// Creates an EF Core Value Converter for extracting the primitive value
/// </summary>
EfValueConverter = 16,
/// <summary>
/// Creates a Dapper TypeHandler for converting to and from the type
/// </summary>
DapperTypeHandler = 32,
/// <summary>
/// Creates a Swagger SchemaFilter for OpenApi documentation
/// </summary>
SwaggerSchemaFilter = 64,
}
}
#endif
#if STRONGLY_TYPED_EMBED_ATTRIBUTES
using System;
namespace Strongly
{
/// <summary>
/// Used to control the default Place on partial structs to make the type a strongly-typed ID
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]
[System.Diagnostics.Conditional("STRONGLY_TYPED_USAGES")]
internal sealed class StronglyDefaultsAttribute : Attribute
{
/// <summary>
/// Set the default values used for strongly typed ids
/// </summary>
/// <param name="backingType">The <see cref="Type"/> to use to store the strongly-typed ID value.
/// Defaults to <see cref="StronglyType.Guid"/></param>
/// <param name="converters">JSON library used to serialize/deserialize strongly-typed ID value.
/// Defaults to <see cref="StronglyConverter.SystemTextJson"/> and <see cref="StronglyConverter.TypeConverter"/></param>
/// <param name="implementations">Interfaces and patterns the strongly typed id should implement
/// Defaults to <see cref="StronglyImplementations.IEquatable"/> and <see cref="StronglyImplementations.IComparable"/></param>
public StronglyDefaultsAttribute(
StronglyType backingType = StronglyType.Default,
StronglyConverter converters = StronglyConverter.Default,
StronglyImplementations implementations = StronglyImplementations.Default)
{
BackingType = backingType;
Converters = converters;
Implementations = implementations;
}
/// <summary>
/// The default <see cref="Type"/> to use to store the strongly-typed ID values.
/// </summary>
public StronglyType BackingType { get; }
/// <summary>
/// The default converters to create for serializing/deserializing strongly-typed ID values.
/// </summary>
public StronglyConverter Converters { get; }
/// <summary>
/// Interfaces and patterns the strongly typed id should implement
/// </summary>
public StronglyImplementations Implementations { get; }
}
}
#endif
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by the Strongly source generator
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
#pragma warning disable 1591 // publicly visible type or member must be documented
namespace StronglyDemo
{
[System.Text.Json.Serialization.JsonConverter(typeof(DayIdSystemTextJsonConverter))]
[System.ComponentModel.TypeConverter(typeof(DayIdTypeConverter))]
readonly partial struct DayId : System.IComparable<DayId>, System.IEquatable<DayId>
{
public int Value { get; }
public DayId(int value)
{
Value = value;
}
public static readonly DayId Empty = new DayId(0);
public bool Equals(DayId other) => this.Value.Equals(other.Value);
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is DayId other && Equals(other);
}
public override int GetHashCode() => Value.GetHashCode();
public override string ToString() => Value.ToString();
public static bool operator ==(DayId a, DayId b) => a.Equals(b);
public static bool operator !=(DayId a, DayId b) => !(a == b);
public static DayId Parse(string value) => new DayId(int.Parse(value));
public static bool TryParse(string value, out DayId result)
{
if (int.TryParse(value, out int parseResult))
{
result = new DayId(parseResult);
return true;
}
result = default;
return false;
}
public int CompareTo(DayId other) => Value.CompareTo(other.Value);
class DayIdTypeConverter : System.ComponentModel.TypeConverter
{
public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
{
return sourceType == typeof(int) || sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
return value switch
{
int intValue => new DayId(intValue),
string stringValue when !string.IsNullOrEmpty(stringValue) && int.TryParse(stringValue, out var result) => new DayId(result),
_ => base.ConvertFrom(context, culture, value),
};
}
public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
{
return sourceType == typeof(int) || sourceType == typeof(string) || base.CanConvertTo(context, sourceType);
}
public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
{
if (value is DayId idValue)
{
if (destinationType == typeof(int))
{
return idValue.Value;
}
if (destinationType == typeof(string))
{
return idValue.Value.ToString();
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
class DayIdSystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter<DayId>
{
public override DayId Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options)
{
return new DayId(reader.GetInt32());
}
public override void Write(System.Text.Json.Utf8JsonWriter writer, DayId value, System.Text.Json.JsonSerializerOptions options)
{
writer.WriteNumberValue(value.Value);
}
}
}
}
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by the Strongly source generator
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
#pragma warning disable 1591 // publicly visible type or member must be documented
namespace StronglyDemo
{
[System.Text.Json.Serialization.JsonConverter(typeof(MonthIdSystemTextJsonConverter))]
[System.ComponentModel.TypeConverter(typeof(MonthIdTypeConverter))]
readonly partial struct MonthId : System.IComparable<MonthId>, System.IEquatable<MonthId>
{
public int Value { get; }
public MonthId(int value)
{
Value = value;
}
public static readonly MonthId Empty = new MonthId(0);
public bool Equals(MonthId other) => this.Value.Equals(other.Value);
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is MonthId other && Equals(other);
}
public override int GetHashCode() => Value.GetHashCode();
public override string ToString() => Value.ToString();
public static bool operator ==(MonthId a, MonthId b) => a.Equals(b);
public static bool operator !=(MonthId a, MonthId b) => !(a == b);
public static MonthId Parse(string value) => new MonthId(int.Parse(value));
public static bool TryParse(string value, out MonthId result)
{
if (int.TryParse(value, out int parseResult))
{
result = new MonthId(parseResult);
return true;
}
result = default;
return false;
}
public int CompareTo(MonthId other) => Value.CompareTo(other.Value);
class MonthIdTypeConverter : System.ComponentModel.TypeConverter
{
public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
{
return sourceType == typeof(int) || sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
return value switch
{
int intValue => new MonthId(intValue),
string stringValue when !string.IsNullOrEmpty(stringValue) && int.TryParse(stringValue, out var result) => new MonthId(result),
_ => base.ConvertFrom(context, culture, value),
};
}
public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
{
return sourceType == typeof(int) || sourceType == typeof(string) || base.CanConvertTo(context, sourceType);
}
public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
{
if (value is MonthId idValue)
{
if (destinationType == typeof(int))
{
return idValue.Value;
}
if (destinationType == typeof(string))
{
return idValue.Value.ToString();
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
class MonthIdSystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter<MonthId>
{
public override MonthId Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options)
{
return new MonthId(reader.GetInt32());
}
public override void Write(System.Text.Json.Utf8JsonWriter writer, MonthId value, System.Text.Json.JsonSerializerOptions options)
{
writer.WriteNumberValue(value.Value);
}
}
}
}
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by the Strongly source generator
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
#pragma warning disable 1591 // publicly visible type or member must be documented
namespace StronglyDemo
{
[System.Text.Json.Serialization.JsonConverter(typeof(YearIdSystemTextJsonConverter))]
[System.ComponentModel.TypeConverter(typeof(YearIdTypeConverter))]
readonly partial struct YearId : System.IComparable<YearId>, System.IEquatable<YearId>
{
public int Value { get; }
public YearId(int value)
{
Value = value;
}
public static readonly YearId Empty = new YearId(0);
public bool Equals(YearId other) => this.Value.Equals(other.Value);
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is YearId other && Equals(other);
}
public override int GetHashCode() => Value.GetHashCode();
public override string ToString() => Value.ToString();
public static bool operator ==(YearId a, YearId b) => a.Equals(b);
public static bool operator !=(YearId a, YearId b) => !(a == b);
public static YearId Parse(string value) => new YearId(int.Parse(value));
public static bool TryParse(string value, out YearId result)
{
if (int.TryParse(value, out int parseResult))
{
result = new YearId(parseResult);
return true;
}
result = default;
return false;
}
public int CompareTo(YearId other) => Value.CompareTo(other.Value);
class YearIdTypeConverter : System.ComponentModel.TypeConverter
{
public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
{
return sourceType == typeof(int) || sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
return value switch
{
int intValue => new YearId(intValue),
string stringValue when !string.IsNullOrEmpty(stringValue) && int.TryParse(stringValue, out var result) => new YearId(result),
_ => base.ConvertFrom(context, culture, value),
};
}
public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
{
return sourceType == typeof(int) || sourceType == typeof(string) || base.CanConvertTo(context, sourceType);
}
public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
{
if (value is YearId idValue)
{
if (destinationType == typeof(int))
{
return idValue.Value;
}
if (destinationType == typeof(string))
{
return idValue.Value.ToString();
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
class YearIdSystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter<YearId>
{
public override YearId Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options)
{
return new YearId(reader.GetInt32());
}
public override void Write(System.Text.Json.Utf8JsonWriter writer, YearId value, System.Text.Json.JsonSerializerOptions options)
{
writer.WriteNumberValue(value.Value);
}
}
}
}
#if STRONGLY_TYPED_EMBED_ATTRIBUTES
using System;
namespace Strongly
{
/// <summary>
/// Interfaces and patterns the strongly typed id should implement
/// </summary>
[Flags]
internal enum StronglyImplementations
{
// Used with HasFlag, so needs to be 1, 2, 4 etc
/// <summary>
/// Don't implement any additional members for the strongly typed ID
/// </summary>
None = 0,
/// <summary>
/// Use the default implementations for the strongly typed Id.
/// This will be the value provided in the <see cref="StronglyDefaultsAttribute"/>, which falls back to
/// <see cref="IEquatable"/> and <see cref="IComparable"/>
/// </summary>
Default = 1,
// ReSharper disable once InconsistentNaming
/// <summary>
/// Implement the <see cref="IEquatable{T}"/> interface
/// </summary>
IEquatable = 2,
// ReSharper disable once InconsistentNaming
/// <summary>
/// Implement the <see cref="IComparable{T}"/> interface
/// </summary>
IComparable = 4,
}
}
#endif
#if STRONGLY_TYPED_EMBED_ATTRIBUTES
using System;
namespace Strongly
{
/// <summary>
/// The <see cref="Type"/> to use to store the value of a strongly-typed ID
/// </summary>
internal enum StronglyType
{
/// <summary>
/// Use the default backing type (either the globally configured default, or Sequential Guid)
/// </summary>
Default = 0,
Guid,
SequentialGuid,
GuidComb,
Int,
String,
Long,
NullableString,
MassTransitNewId,
BigInteger,
Decimal,
}
}
#endif
Code and pdf at
https://ignatandrei.github.io/RSCG_Examples/v2/docs/Strongly