RSCG – Strongly
RSCG – Strongly
name | Strongly |
nuget | https://www.nuget.org/packages/Strongly/ |
link | https://github.com/lucasteles/Strongly/ |
author | Lucas Teles |
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
Leave a Reply