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