Category: roslyn

RSCG – Microsoft.Extensions.Configuration.Binder

RSCG – Microsoft.Extensions.Configuration.Binder    

name Microsoft.Extensions.Configuration.Binder
nuget https://www.nuget.org/packages/Microsoft.Extensions.Configuration.Binder/
link https://github.com/dotnet/runtime
author Microsoft

Generating Binding for configuration files

 

This is how you can use Microsoft.Extensions.Configuration.Binder .

The code that you start with is

  <Project Sdk="Microsoft.NET.Sdk.Web">    <PropertyGroup>     <TargetFramework>net8.0</TargetFramework>     <Nullable>enable</Nullable>     <ImplicitUsings>enable</ImplicitUsings>     <InvariantGlobalization>true</InvariantGlobalization>   </PropertyGroup>    <ItemGroup> 	  <!--<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />-->     <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" />     <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />   </ItemGroup> 	<PropertyGroup> 		<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator> 	</PropertyGroup> 	<PropertyGroup> 		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> 		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath> 	</PropertyGroup>   </Project>   

The code that you will use is

  using ConfigBinderDemo; using Microsoft.Extensions.Options;  var builder = WebApplication.CreateBuilder(args);  // Add services to the container. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen();  var app = builder.Build();  // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) {     app.UseSwagger();     app.UseSwaggerUI(); } builder.Services.AddOptions<MyAppOptions>()             .BindConfiguration(MyAppOptions.ConfigName); app.MapGet("/nameApp", (IOptions<MyAppOptions> opt) => {     try     {         var val = opt.Value.AppDisplayName;         return val;     }     catch (OptionsValidationException ex)     {         var problems = ex.Failures.ToArray();         return string.Join(",", problems);     }  }) .WithName("GetWeatherForecast") .WithOpenApi();  app.Run();  internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) {     public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); }   
  using System.Diagnostics;  namespace ConfigBinderDemo;  [DebuggerDisplay("{AppDisplayName}")] public class MyAppOptions {     public const string ConfigName = "MyAppOptionsInConfig";     public string AppDisplayName { get; set; } = string.Empty;  }   

  The code that is generated is

 // <auto-generated/> #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.  namespace System.Runtime.CompilerServices {     using System;     using System.CodeDom.Compiler;      [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "8.0.9.3103")]     [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]     file sealed class InterceptsLocationAttribute : Attribute     {         public InterceptsLocationAttribute(string filePath, int line, int column)         {         }     } }  namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration {     using ConfigBinderDemo;     using Microsoft.Extensions.Configuration;     using Microsoft.Extensions.DependencyInjection;     using Microsoft.Extensions.Options;     using System;     using System.CodeDom.Compiler;     using System.Collections.Generic;     using System.Globalization;     using System.Runtime.CompilerServices;      [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "8.0.9.3103")]     file static class BindingExtensions     {         #region OptionsBuilder<TOptions> extensions.         /// <summary>Registers the dependency injection container to bind <typeparamref name="TOptions"/> against the <see cref="IConfiguration"/> obtained from the DI service provider.</summary>         [InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\ConfigBinder\src\ConfigBinderDemo\Program.cs", 20, 14)]         public static OptionsBuilder<TOptions> BindConfiguration<TOptions>(this OptionsBuilder<TOptions> optionsBuilder, string configSectionPath, Action<BinderOptions>? configureBinder = null) where TOptions : class         {             if (optionsBuilder is null)             {                 throw new ArgumentNullException(nameof(optionsBuilder));             }              if (configSectionPath is null)             {                 throw new ArgumentNullException(nameof(configSectionPath));             }              optionsBuilder.Configure<IConfiguration>((instance, config) =>             {                 if (config is null)                 {                     throw new ArgumentNullException(nameof(config));                 }                  IConfiguration section = string.Equals(string.Empty, configSectionPath, StringComparison.OrdinalIgnoreCase) ? config : config.GetSection(configSectionPath);                 BindCoreMain(section, instance, typeof(TOptions), configureBinder);             });              optionsBuilder.Services.AddSingleton<IOptionsChangeTokenSource<TOptions>, ConfigurationChangeTokenSource<TOptions>>();             return optionsBuilder;         }         #endregion OptionsBuilder<TOptions> extensions.          #region Core binding extensions.         private readonly static Lazy<HashSet<string>> s_configKeys_MyAppOptions = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "AppDisplayName" });          public static void BindCoreMain(IConfiguration configuration, object instance, Type type, Action<BinderOptions>? configureOptions)         {             if (instance is null)             {                 return;             }              if (!HasValueOrChildren(configuration))             {                 return;             }              BinderOptions? binderOptions = GetBinderOptions(configureOptions);              if (type == typeof(MyAppOptions))             {                 var temp = (MyAppOptions)instance;                 BindCore(configuration, ref temp, defaultValueIfNotFound: false, binderOptions);                 return;             }              throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");         }          public static void BindCore(IConfiguration configuration, ref MyAppOptions instance, bool defaultValueIfNotFound, BinderOptions? binderOptions)         {             ValidateConfigurationKeys(typeof(MyAppOptions), s_configKeys_MyAppOptions, configuration, binderOptions);              if (configuration["AppDisplayName"] is string value1)             {                 instance.AppDisplayName = value1;             }         }           /// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>         public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)         {             if (binderOptions?.ErrorOnUnknownConfiguration is true)             {                 List<string>? temp = null;                          foreach (IConfigurationSection section in configuration.GetChildren())                 {                     if (!keys.Value.Contains(section.Key))                     {                         (temp ??= new List<string>()).Add($"'{section.Key}'");                     }                 }                          if (temp is not null)                 {                     throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}");                 }             }         }          public static bool HasValueOrChildren(IConfiguration configuration)         {             if ((configuration as IConfigurationSection)?.Value is not null)             {                 return true;             }             return AsConfigWithChildren(configuration) is not null;         }          public static IConfiguration? AsConfigWithChildren(IConfiguration configuration)         {             foreach (IConfigurationSection _ in configuration.GetChildren())             {                 return configuration;             }             return null;         }          public static BinderOptions? GetBinderOptions(Action<BinderOptions>? configureOptions)         {             if (configureOptions is null)             {                 return null;             }                      BinderOptions binderOptions = new();             configureOptions(binderOptions);                      if (binderOptions.BindNonPublicProperties)             {                 throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");             }                      return binderOptions;         }         #endregion Core binding extensions.     } }  

Code and pdf at https://ignatandrei.github.io/RSCG_Examples/v2/docs/Microsoft.Extensions.Configuration.Binder

RSCG – Microsoft.Extensions.Options.Generators.OptionsValidatorGenerator

RSCG – Microsoft.Extensions.Options.Generators.OptionsValidatorGenerator    

name Microsoft.Extensions.Options.Generators.OptionsValidatorGenerator
nuget https://www.nuget.org/packages/Microsoft.Extensions.Options
link https://learn.microsoft.com/en-us/dotnet/core/extensions/options-validation-generator
author Microsoft

Generating the validation for data annotations on options classes.

 

This is how you can use Microsoft.Extensions.Options.Generators.OptionsValidatorGenerator .

The code that you start with is

  <Project Sdk="Microsoft.NET.Sdk">    <PropertyGroup>     <TargetFramework>net8.0</TargetFramework>     <ImplicitUsings>enable</ImplicitUsings>     <Nullable>enable</Nullable>   </PropertyGroup> 	<ItemGroup> 		<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.0" /> 	</ItemGroup> 	<PropertyGroup> 		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> 		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath> 	</PropertyGroup> 	 </Project>   

The code that you will use is

  namespace DemoValidatorObj;  [OptionsValidator] public partial class ValidatorForMyApp     : IValidateOptions<MyAppOptions> { }  //public class SecondModelNoNamespace //{ //    [Required] //    [MinLength(5)] //    public string P4 { get; set; } = string.Empty; //}   //[OptionsValidator] //public partial class SecondValidatorNoNamespace //    : IValidateOptions<SecondModelNoNamespace> //{ //}     
  namespace DemoValidatorObj;  [DebuggerDisplay("{AppDisplayName}")] public class MyAppOptions {     public const string ConfigName = "MyAppOptionsInConfig";     [Required]     [MinLength(3)]     public string AppDisplayName { get; set; } = string.Empty;      //[ValidateObjectMembers(     //    typeof(SecondValidatorNoNamespace))]     //public SecondModelNoNamespace? P2 { get; set; } }  //[OptionsValidator] //public partial class SecondValidatorNoNamespace //    : IValidateOptions<SecondModelNoNamespace> //{ //}  

  The code that is generated is

      // <auto-generated/>     #nullable enable     #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103     namespace DemoValidatorObj {     partial class ValidatorForMyApp     {         /// <summary>         /// Validates a specific named options instance (or all when <paramref name="name"/> is <see langword="null" />).         /// </summary>         /// <param name="name">The name of the options instance being validated.</param>         /// <param name="options">The options instance.</param>         /// <returns>Validation result.</returns>         [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "8.0.9.3103")]         [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",              Justification = "The created ValidationContext object is used in a way that never call reflection")]         public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::DemoValidatorObj.MyAppOptions options)         {             global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;             var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);             var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();             var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(2);              context.MemberName = "AppDisplayName";             context.DisplayName = string.IsNullOrEmpty(name) ? "MyAppOptions.AppDisplayName" : $"{name}.AppDisplayName";             validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1);             validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);             if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.AppDisplayName, context, validationResults, validationAttributes))             {                 (builder ??= new()).AddResults(validationResults);             }              return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();         }     } } namespace __OptionValidationStaticInstances {     [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "8.0.9.3103")]     file static class __Attributes     {         internal static readonly global::System.ComponentModel.DataAnnotations.RequiredAttribute A1 = new global::System.ComponentModel.DataAnnotations.RequiredAttribute();          internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__MinLengthAttribute A2 = new __OptionValidationGeneratedAttributes.__SourceGen__MinLengthAttribute(             (int)3);     } } namespace __OptionValidationStaticInstances {     [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "8.0.9.3103")]     file static class __Validators     {     } } namespace __OptionValidationGeneratedAttributes {     [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "8.0.9.3103")]     [global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]     file class __SourceGen__MinLengthAttribute : global::System.ComponentModel.DataAnnotations.ValidationAttribute     {         private static string DefaultErrorMessageString => "The field {0} must be a string or array type with a minimum length of '{1}'.";          public __SourceGen__MinLengthAttribute(int length) : base(() => DefaultErrorMessageString) { Length = length; }         public int Length { get; }         public override bool IsValid(object? value)         {             if (Length < -1)             {                 throw new global::System.InvalidOperationException("MinLengthAttribute must have a Length value that is zero or greater.");             }             if (value == null)             {                 return true;             }              int length;             if (value is string stringValue)             {                 length = stringValue.Length;             }             else if (value is System.Collections.ICollection collectionValue)             {                 length = collectionValue.Count;             }             else             {                 throw new global::System.InvalidCastException($"The field of type {value.GetType()} must be a string, array, or ICollection type.");             }              return length >= Length;         }         public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, ErrorMessageString, name, Length);     } }  

Code and pdf at https://ignatandrei.github.io/RSCG_Examples/v2/docs/Microsoft.Extensions.Options.Generators.OptionsValidatorGenerator

RSCG – Biwen.AutoClassGen

RSCG – Biwen.AutoClassGen    

name Biwen.AutoClassGen
nuget https://www.nuget.org/packages/Biwen.AutoClassGen/
link https://github.com/vipwan/Biwen.AutoClassGen
author vipwan

Generating properties from interface to class.

 

This is how you can use Biwen.AutoClassGen .

The code that you start with is

  <Project Sdk="Microsoft.NET.Sdk">    <PropertyGroup>     <OutputType>Exe</OutputType>     <TargetFramework>net8.0</TargetFramework>     <ImplicitUsings>enable</ImplicitUsings>     <Nullable>enable</Nullable>   </PropertyGroup>    <ItemGroup>     <PackageReference Include="Biwen.AutoClassGen" Version="1.0.0.6" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />     <PackageReference Include="Biwen.AutoClassGen.Attributes" Version="1.0.0" /> 	     </ItemGroup>  	<PropertyGroup> 		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> 		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath> 	</PropertyGroup> 	 </Project>   

The code that you will use is

  using FromInterface;  Console.WriteLine("Hello, World!"); Person p = new(); p.FirstName = "Andrei"; p.LastName = "Ignat"; Console.WriteLine(p.FullName());  

  The code that is generated is

 // <auto-generated /> // author:vipwan@outlook.com 万雅虎 // issue:https://github.com/vipwan/Biwen.AutoClassGen/issues // 如果你在使用中遇到问题,请第一时间issue,谢谢! // This file is generated by Biwen.AutoClassGen.SourceGenerator using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using FromInterface;  #pragma warning disable namespace FromInterface {     public partial class Person : IPerson2     {         /// <inheritdoc cref = "IPerson.FirstName"/>         [System.ComponentModel.DataAnnotations.StringLengthAttribute(100)]         [System.ComponentModel.DescriptionAttribute("person first name")]         public string FirstName { get; set; }         /// <inheritdoc cref = "IPerson.LastName"/>         public string LastName { get; set; }     } } #pragma warning restore  

Code and pdf at https://ignatandrei.github.io/RSCG_Examples/v2/docs/Biwen.AutoClassGen

RSCG – PrimaryParameter

RSCG – PrimaryParameter    

name PrimaryParameter
nuget https://www.nuget.org/packages/FaustVX.PrimaryParameter.SG
link https://github.com/FaustVX/PrimaryParameter
author FaustVX

Generating properties from .NET 8 constructor parameters

 

This is how you can use PrimaryParameter .

The code that you start with is

  <Project Sdk="Microsoft.NET.Sdk">    <PropertyGroup>     <OutputType>Exe</OutputType>     <TargetFramework>net8.0</TargetFramework>     <ImplicitUsings>enable</ImplicitUsings>     <Nullable>enable</Nullable>   </PropertyGroup>  	<PropertyGroup> 		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> 		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath> 	</PropertyGroup>  	<ItemGroup> 	  <PackageReference Include="FaustVX.PrimaryParameter.SG" Version="1.2.0" OutputItemType="Analyzer" ReferenceOutputAssembly="false"  /> 	</ItemGroup> </Project>   

The code that you will use is

  using QuickConstructorDemo;  var p = new Person("Andrei", "Ignat");  Console.WriteLine(p.FullName());  
  using PrimaryParameter.SG; namespace QuickConstructorDemo; internal partial class Person([Property]string FirstName,[Field(Name ="_LastName",Scope ="public")]string? LastName=null) {     //private readonly string FirstName;     //private readonly string? LastName;          public string FullName() => $"{FirstName} {_LastName}";      }   

  The code that is generated is

 namespace QuickConstructorDemo {     partial class Person     {         public string FirstName { get; init; } = FirstName;     } } namespace QuickConstructorDemo {     partial class Person     {         public readonly string _LastName = LastName;     } }  
 using global::System; namespace PrimaryParameter.SG {     [AttributeUsage(AttributeTargets.Parameter, Inherited = false, AllowMultiple = true)]     sealed class FieldAttribute : Attribute     {         public string Name { get; init; }         public string AssignFormat { get; init; }         public Type Type { get; init; }         public bool IsReadonly { get; init; }         public string Scope { get; init; }     } } 
 using global::System; namespace PrimaryParameter.SG {     [AttributeUsage(AttributeTargets.Parameter, Inherited = false, AllowMultiple = true)]     sealed class PropertyAttribute : Attribute     {         public string Name { get; init; }         public string AssignFormat { get; init; }         public Type Type { get; init; }         public bool WithInit { get; init; }         public string Scope { get; init; }     } } 
 using global::System; namespace PrimaryParameter.SG {     [AttributeUsage(AttributeTargets.Parameter, Inherited = false, AllowMultiple = true)]     sealed class RefFieldAttribute : Attribute     {         public string Name { get; init; }         public string Scope { get; init; }         public bool IsReadonlyRef { get; init; }         public bool IsRefReadonly { get; init; }     } } 

Code and pdf at https://ignatandrei.github.io/RSCG_Examples/v2/docs/PrimaryParameter

RSCG – jsonConverterSourceGenerator

RSCG – jsonConverterSourceGenerator    

name jsonConverterSourceGenerator
nuget https://www.nuget.org/packages/Aviationexam.GeneratedJsonConverters.SourceGenerator/
link https://github.com/aviationexam/json-converter-source-generator
author Aviationexam

Json Polymorphic generator

  

This is how you can use jsonConverterSourceGenerator .

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>
  </itemgroup>
	<itemgroup>
		<packagereference privateassets="all" version="0.1.11" include="Aviationexam.GeneratedJsonConverters.SourceGenerator">
	</packagereference>
	<propertygroup>
		<emitcompilergeneratedfiles>true</emitcompilergeneratedfiles>
		<compilergeneratedfilesoutputpath>$(BaseIntermediateOutputPath)\GX</compilergeneratedfilesoutputpath>
	</propertygroup>

</itemgroup>


The code that you will use is


//https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism?pivots=dotnet-7-0
using JsonPolymorphicGeneratorDemo;
using System.Text.Json;

Person[] persons = new Person[2];
persons[0] = new Student() { Name="Student Ignat"};

persons[1] = new Teacher() { Name = "Teacher Ignat" };
//JsonSerializerOptions opt = new ()
//{
//    WriteIndented = true
//};
//var ser = JsonSerializer.Serialize(persons, opt);

var ser = JsonSerializer.Serialize(persons, ProjectJsonSerializerContext.Default.Options);

Console.WriteLine(ser);
var p = JsonSerializer.Deserialize<person  &#91;&#93;>(ser,ProjectJsonSerializerContext.Default.Options);
if(p != null)
foreach (var item in p)
{
    Console.WriteLine(item.Data());
}



namespace JsonPolymorphicGeneratorDemo;

[Aviationexam.GeneratedJsonConverters.Attributes.JsonPolymorphic]
[Aviationexam.GeneratedJsonConverters.Attributes.JsonDerivedType(typeof(Student))]
[Aviationexam.GeneratedJsonConverters.Attributes.JsonDerivedType(typeof(Teacher))]
public abstract partial class Person
{
    
    public string? Name { get; set; }
    public abstract string Data();
}

public class Teacher : Person
{
    public override string Data()
    {
        return "Class Teacher:" + Name;
    }
}
public class Student : Person
{
    public override string Data()
    {
        return "Class Student:" + Name;
    }
}



//https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism?pivots=dotnet-7-0
using JsonPolymorphicGeneratorDemo;
using System.Text.Json.Serialization;

[JsonSourceGenerationOptions(
    WriteIndented = true,
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
    GenerationMode = JsonSourceGenerationMode.Default
)]
[JsonSerializable(typeof(Person[]))]
[JsonSerializable(typeof(Person))]
[JsonSerializable(typeof(Student))]
[JsonSerializable(typeof(Teacher))]
public partial class ProjectJsonSerializerContext : JsonSerializerContext
{
    static ProjectJsonSerializerContext()
    {
        foreach (var converter in GetPolymorphicConverters())
        {
            s_defaultOptions.Converters.Add(converter);
        }
    }
}

   The code that is generated is

// ReSharper disable once CheckNamespace
namespace Aviationexam.GeneratedJsonConverters.Attributes;

/// <summary>
/// When placed on an enum, indicates that generator should not report missing <see cref="EnumJsonConverterAttribute">
/// </see></summary>
[System.AttributeUsage(System.AttributeTargets.Enum, AllowMultiple = false, Inherited = false)]
internal sealed class DisableEnumJsonConverterAttribute : System.Text.Json.Serialization.JsonAttribute
{
}

// ReSharper disable once RedundantNullableDirective

#nullable enable

using Aviationexam.GeneratedJsonConverters.Attributes;
using System;

namespace Aviationexam.GeneratedJsonConverters;

[Flags]
[DisableEnumJsonConverter]
internal enum EnumDeserializationStrategy : byte
{
    ProjectDefault = 0,
    UseBackingType = 1 &lt;&lt; 0,
    UseEnumName = 1 &lt;&lt; 1,
}

#nullable enable

namespace Aviationexam.GeneratedJsonConverters.Attributes;

/// <summary>
/// When placed on an enum, indicates that the type should be serialized using generated enum convertor.
/// </summary>
[System.AttributeUsage(System.AttributeTargets.Enum, AllowMultiple = false, Inherited = false)]
internal sealed class EnumJsonConverterAttribute : System.Text.Json.Serialization.JsonAttribute
{
    /// <summary>
    /// Configure serialization strategy
    /// </summary>
    public EnumSerializationStrategy SerializationStrategy { get; set; } = EnumSerializationStrategy.ProjectDefault;

    /// <summary>
    /// Configure deserialization strategy
    /// </summary>
    public EnumDeserializationStrategy DeserializationStrategy { get; set; } = EnumDeserializationStrategy.ProjectDefault;
}
// ReSharper disable once RedundantNullableDirective

#nullable enable

using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Aviationexam.GeneratedJsonConverters;

internal abstract class EnumJsonConvertor<t  , tbackingtype=""> : JsonConverter<t>
    where T : struct, Enum
    where TBackingType : struct
{
    protected abstract TypeCode BackingTypeTypeCode { get; }

    protected abstract EnumDeserializationStrategy DeserializationStrategy { get; }

    protected abstract EnumSerializationStrategy SerializationStrategy { get; }

    public abstract bool TryToEnum(ReadOnlySpan<byte> enumName, out T value);

    public abstract bool TryToEnum(TBackingType numericValue, out T value);

    public abstract TBackingType ToBackingType(T value);

    public abstract ReadOnlySpan<byte> ToFirstEnumName(T value);

    public override T Read(
        ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options
    )
    {
        if (
            reader.TokenType is JsonTokenType.String
            &amp;&amp; DeserializationStrategy.HasFlag(EnumDeserializationStrategy.UseEnumName)
        )
        {
            var enumName = reader.ValueSpan;

            if (TryToEnum(enumName, out var enumValue))
            {
                return enumValue;
            }

            var stringValue = Encoding.UTF8.GetString(enumName.ToArray());

            throw new JsonException($"Undefined mapping of '{stringValue}' to enum '{typeof(T).FullName}'");
        }

        if (reader.TokenType is JsonTokenType.Number)
        {
            var numericValue = ReadAsNumber(ref reader);

            if (numericValue.HasValue)
            {
                if (TryToEnum(numericValue.Value, out var enumValue))
                {
                    return enumValue;
                }

                throw new JsonException($"Undefined mapping of '{numericValue}' to enum '{{enumFullName}}'");
            }
        }

        var value = Encoding.UTF8.GetString(reader.ValueSpan.ToArray());

        throw new JsonException($"Unable to deserialize {value}('{reader.TokenType}') into {typeof(T).Name}");
    }

    public override T ReadAsPropertyName(
        ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options
    )
    {
        if (
            reader.TokenType is JsonTokenType.PropertyName
            &amp;&amp; DeserializationStrategy.HasFlag(EnumDeserializationStrategy.UseEnumName)
        )
        {
            var enumName = reader.ValueSpan;

            if (TryToEnum(enumName, out var enumValue))
            {
                return enumValue;
            }
        }

        var value = Encoding.UTF8.GetString(reader.ValueSpan.ToArray());

        if (
            reader.TokenType is JsonTokenType.PropertyName
            &amp;&amp; DeserializationStrategy.HasFlag(EnumDeserializationStrategy.UseBackingType)
        )
        {
            var numericValue = ParseAsNumber(value);

            if (numericValue.HasValue)
            {
                if (TryToEnum(numericValue.Value, out var enumValue))
                {
                    return enumValue;
                }
            }
        }

        throw new JsonException($"Unable to deserialize {value}('{reader.TokenType}') into {typeof(T).Name}");
    }

    private TBackingType? ReadAsNumber(ref Utf8JsonReader reader) =&gt; BackingTypeTypeCode switch
    {
        TypeCode.SByte =&gt; reader.GetSByte() is var numericValue ? Unsafe.As<sbyte  , tbackingtype="">(ref numericValue) : null,
        TypeCode.Byte =&gt; reader.GetByte() is var numericValue ? Unsafe.As<byte  , tbackingtype="">(ref numericValue) : null,
        TypeCode.Int16 =&gt; reader.GetInt16() is var numericValue ? Unsafe.As<short  , tbackingtype="">(ref numericValue) : null,
        TypeCode.UInt16 =&gt; reader.GetUInt16() is var numericValue ? Unsafe.As<ushort  , tbackingtype="">(ref numericValue) : null,
        TypeCode.Int32 =&gt; reader.GetInt32() is var numericValue ? Unsafe.As<int  , tbackingtype="">(ref numericValue) : null,
        TypeCode.UInt32 =&gt; reader.GetUInt32() is var numericValue ? Unsafe.As<uint  , tbackingtype="">(ref numericValue) : null,
        TypeCode.Int64 =&gt; reader.GetInt64() is var numericValue ? Unsafe.As<long  , tbackingtype="">(ref numericValue) : null,
        TypeCode.UInt64 =&gt; reader.GetUInt64() is var numericValue ? Unsafe.As<ulong  , tbackingtype="">(ref numericValue) : null,
        _ =&gt; throw new ArgumentOutOfRangeException(nameof(BackingTypeTypeCode), BackingTypeTypeCode, $"Unexpected TypeCode {BackingTypeTypeCode}")
    };

    private TBackingType? ParseAsNumber(
        string value
    ) =&gt; BackingTypeTypeCode switch
    {
        TypeCode.SByte =&gt; sbyte.TryParse(value, out var numericValue) ? Unsafe.As<sbyte  , tbackingtype="">(ref numericValue) : null,
        TypeCode.Byte =&gt; byte.TryParse(value, out var numericValue) ? Unsafe.As<byte  , tbackingtype="">(ref numericValue) : null,
        TypeCode.Int16 =&gt; short.TryParse(value, out var numericValue) ? Unsafe.As<short  , tbackingtype="">(ref numericValue) : null,
        TypeCode.UInt16 =&gt; ushort.TryParse(value, out var numericValue) ? Unsafe.As<ushort  , tbackingtype="">(ref numericValue) : null,
        TypeCode.Int32 =&gt; int.TryParse(value, out var numericValue) ? Unsafe.As<int  , tbackingtype="">(ref numericValue) : null,
        TypeCode.UInt32 =&gt; uint.TryParse(value, out var numericValue) ? Unsafe.As<uint  , tbackingtype="">(ref numericValue) : null,
        TypeCode.Int64 =&gt; long.TryParse(value, out var numericValue) ? Unsafe.As<long  , tbackingtype="">(ref numericValue) : null,
        TypeCode.UInt64 =&gt; ulong.TryParse(value, out var numericValue) ? Unsafe.As<ulong  , tbackingtype="">(ref numericValue) : null,
        _ =&gt; throw new ArgumentOutOfRangeException(nameof(BackingTypeTypeCode), BackingTypeTypeCode, $"Unexpected TypeCode {BackingTypeTypeCode}")
    };

    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
    {
        if (SerializationStrategy is EnumSerializationStrategy.BackingType)
        {
            WriteAsBackingType(writer, value, options);
        }
        else if (SerializationStrategy is EnumSerializationStrategy.FirstEnumName)
        {
            WriteAsFirstEnumName(writer, value, options);
        }
        else
        {
            throw new ArgumentOutOfRangeException(nameof(SerializationStrategy), SerializationStrategy, "Unknown serialization strategy");
        }
    }

    public override void WriteAsPropertyName(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
    {
        if (SerializationStrategy is EnumSerializationStrategy.BackingType)
        {
            WriteAsPropertyNameAsBackingType(writer, value, options);
        }
        else if (SerializationStrategy is EnumSerializationStrategy.FirstEnumName)
        {
            WriteAsPropertyNameAsFirstEnumName(writer, value, options);
        }
        else
        {
            throw new ArgumentOutOfRangeException(nameof(SerializationStrategy), SerializationStrategy, "Unknown serialization strategy");
        }
    }

    private void WriteAsBackingType(
        Utf8JsonWriter writer,
        T value,
        [SuppressMessage("ReSharper", "UnusedParameter.Local")]
        JsonSerializerOptions options
    )
    {
        var numericValue = ToBackingType(value);

        switch (BackingTypeTypeCode)
        {
            case TypeCode.SByte:
                writer.WriteNumberValue(Unsafe.As<tbackingtype  , sbyte="">(ref numericValue));
                break;
            case TypeCode.Byte:
                writer.WriteNumberValue(Unsafe.As<tbackingtype  , byte="">(ref numericValue));
                break;
            case TypeCode.Int16:
                writer.WriteNumberValue(Unsafe.As<tbackingtype  , short="">(ref numericValue));
                break;
            case TypeCode.UInt16:
                writer.WriteNumberValue(Unsafe.As<tbackingtype  , ushort="">(ref numericValue));
                break;
            case TypeCode.Int32:
                writer.WriteNumberValue(Unsafe.As<tbackingtype  , int="">(ref numericValue));
                break;
            case TypeCode.UInt32:
                writer.WriteNumberValue(Unsafe.As<tbackingtype  , uint="">(ref numericValue));
                break;
            case TypeCode.Int64:
                writer.WriteNumberValue(Unsafe.As<tbackingtype  , long="">(ref numericValue));
                break;
            case TypeCode.UInt64:
                writer.WriteNumberValue(Unsafe.As<tbackingtype  , ulong="">(ref numericValue));
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(BackingTypeTypeCode), BackingTypeTypeCode, $"Unexpected TypeCode {BackingTypeTypeCode}");
        }
    }

    private void WriteAsPropertyNameAsBackingType(
        Utf8JsonWriter writer,
        T value,
        [SuppressMessage("ReSharper", "UnusedParameter.Local")]
        JsonSerializerOptions options
    )
    {
        var numericValue = ToBackingType(value);

        writer.WritePropertyName($"{numericValue}");
    }

    private void WriteAsFirstEnumName(
        Utf8JsonWriter writer,
        T value,
        [SuppressMessage("ReSharper", "UnusedParameter.Local")]
        JsonSerializerOptions options
    )
    {
        var enumValue = ToFirstEnumName(value);

        writer.WriteStringValue(enumValue);
    }

    private void WriteAsPropertyNameAsFirstEnumName(
        Utf8JsonWriter writer,
        T value,
        [SuppressMessage("ReSharper", "UnusedParameter.Local")]
        JsonSerializerOptions options
    )
    {
        var enumValue = ToFirstEnumName(value);

        writer.WritePropertyName(enumValue);
    }
}

// ReSharper disable once RedundantNullableDirective

#nullable enable

using Aviationexam.GeneratedJsonConverters.Attributes;

namespace Aviationexam.GeneratedJsonConverters;

[DisableEnumJsonConverter]
internal enum EnumSerializationStrategy : byte
{
    ProjectDefault,
    BackingType,
    FirstEnumName,
}

// ReSharper disable once RedundantNullableDirective

#nullable enable

namespace Aviationexam.GeneratedJsonConverters;

internal readonly struct DiscriminatorStruct<t> : IDiscriminatorStruct
{
    public T Value { get; init; }

    public DiscriminatorStruct(T value)
    {
        Value = value;
    }
}

// ReSharper disable once RedundantNullableDirective

#nullable enable

namespace Aviationexam.GeneratedJsonConverters;

internal interface IDiscriminatorStruct
{
}

#nullable enable

namespace Aviationexam.GeneratedJsonConverters.Attributes;

/// <summary>
/// This is a copy of System.Text.Json.Serialization.JsonDerivedTypeAttribute.
/// It's purpose is to replace this attribute to silence System.Text.Json.Serialization.Metadata.PolymorphicTypeResolver{ThrowHelper.ThrowNotSupportedException_BaseConverterDoesNotSupportMetadata}
///
/// When placed on a type declaration, indicates that the specified subtype should be opted into polymorphic serialization.
/// </summary>
[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Interface, AllowMultiple = true, Inherited = false)]
internal class JsonDerivedTypeAttribute : System.Text.Json.Serialization.JsonAttribute
{
    /// <summary>
    /// Initializes a new attribute with specified parameters.
    /// </summary>
    /// <param name="derivedType">A derived type that should be supported in polymorphic serialization of the declared based type.
    public JsonDerivedTypeAttribute(System.Type derivedType)
    {
        DerivedType = derivedType;
    }

    /// <summary>
    /// Initializes a new attribute with specified parameters.
    /// </summary>
    /// <param name="derivedType">A derived type that should be supported in polymorphic serialization of the declared base type.
    /// <param name="typeDiscriminator">The type discriminator identifier to be used for the serialization of the subtype.
    public JsonDerivedTypeAttribute(System.Type derivedType, string typeDiscriminator)
    {
        DerivedType = derivedType;
        TypeDiscriminator = typeDiscriminator;
    }

    /// <summary>
    /// Initializes a new attribute with specified parameters.
    /// </summary>
    /// <param name="derivedType">A derived type that should be supported in polymorphic serialization of the declared base type.
    /// <param name="typeDiscriminator">The type discriminator identifier to be used for the serialization of the subtype.
    public JsonDerivedTypeAttribute(System.Type derivedType, int typeDiscriminator)
    {
        DerivedType = derivedType;
        TypeDiscriminator = typeDiscriminator;
    }

    /// <summary>
    /// A derived type that should be supported in polymorphic serialization of the declared base type.
    /// </summary>
    public System.Type DerivedType { get; }

    /// <summary>
    /// The type discriminator identifier to be used for the serialization of the subtype.
    /// </summary>
    public object? TypeDiscriminator { get; }
}

[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Interface, AllowMultiple = true, Inherited = false)]
internal class JsonDerivedTypeAttribute<tderivedtype> : JsonDerivedTypeAttribute
{
    /// <summary>
    /// Initializes a new attribute with specified parameters.
    /// </summary>
    public JsonDerivedTypeAttribute() : base(typeof(TDerivedType))
    {
    }

    /// <summary>
    /// Initializes a new attribute with specified parameters.
    /// </summary>
    /// <param name="typeDiscriminator">The type discriminator identifier to be used for the serialization of the subtype.
    public JsonDerivedTypeAttribute(string typeDiscriminator) : base(typeof(TDerivedType), typeDiscriminator)
    {
    }

    /// <summary>
    /// Initializes a new attribute with specified parameters.
    /// </summary>
    /// <param name="typeDiscriminator">The type discriminator identifier to be used for the serialization of the subtype.
    public JsonDerivedTypeAttribute(int typeDiscriminator) : base(typeof(TDerivedType), typeDiscriminator)
    {
    }
}
#nullable enable

namespace Aviationexam.GeneratedJsonConverters.Attributes;

/// <summary>
/// This is a copy of System.Text.Json.Serialization.JsonPolymorphicAttribute.
/// It's purpose is to replace this attribute to silence System.Text.Json.Serialization.Metadata.PolymorphicTypeResolver{ThrowHelper.ThrowNotSupportedException_BaseConverterDoesNotSupportMetadata}
///
/// When placed on a type, indicates that the type should be serialized polymorphically.
/// </summary>
[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
internal sealed class JsonPolymorphicAttribute : System.Text.Json.Serialization.JsonAttribute
{
    /// <summary>
    /// Gets or sets a custom type discriminator property name for the polymorhic type.
    /// Uses the default '$type' property name if left unset.
    /// </summary>
    public string? TypeDiscriminatorPropertyName { get; set; }

    /// <summary>
    /// Gets or sets the behavior when serializing an undeclared derived runtime type.
    /// </summary>
    public System.Text.Json.Serialization.JsonUnknownDerivedTypeHandling UnknownDerivedTypeHandling { get; set; }

    /// <summary>
    /// When set to <see langword="true">, instructs the deserializer to ignore any
    /// unrecognized type discriminator id's and reverts to the contract of the base type.
    /// Otherwise, it will fail the deserialization.
    /// </see></summary>
    public bool IgnoreUnrecognizedTypeDiscriminators { get; set; }
}
#nullable enable

namespace PolymorphicGlobalNamespace;

internal class PersonJsonPolymorphicConverter : Aviationexam.GeneratedJsonConverters.PolymorphicJsonConvertor<global::jsonpolymorphicgeneratordemo.person>
{
    protected override System.ReadOnlySpan<byte> GetDiscriminatorPropertyName() =&gt; "$type"u8;

    protected override System.Type GetTypeForDiscriminator(
        Aviationexam.GeneratedJsonConverters.IDiscriminatorStruct discriminator
    ) =&gt; discriminator switch
    {
        Aviationexam.GeneratedJsonConverters.DiscriminatorStruct<string> { Value: "Student" } =&gt; typeof(JsonPolymorphicGeneratorDemo.Student),
        Aviationexam.GeneratedJsonConverters.DiscriminatorStruct<string> { Value: "Teacher" } =&gt; typeof(JsonPolymorphicGeneratorDemo.Teacher),

        _ =&gt; throw new System.ArgumentOutOfRangeException(nameof(discriminator), discriminator, null),
    };

    protected override Aviationexam.GeneratedJsonConverters.IDiscriminatorStruct GetDiscriminatorForType(
        System.Type type
    )
    {
        if (type == typeof(JsonPolymorphicGeneratorDemo.Student))
        {
            return new Aviationexam.GeneratedJsonConverters.DiscriminatorStruct<string>("Student");
        }
        if (type == typeof(JsonPolymorphicGeneratorDemo.Teacher))
        {
            return new Aviationexam.GeneratedJsonConverters.DiscriminatorStruct<string>("Teacher");
        }

        throw new System.ArgumentOutOfRangeException(nameof(type), type, null);
    }
}
// ReSharper disable once RedundantNullableDirective

#nullable enable

using System;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Aviationexam.GeneratedJsonConverters;

internal abstract class PolymorphicJsonConvertor<t> : JsonConverter<t> where T : class
{
    private readonly Type _polymorphicType = typeof(T);

    protected abstract ReadOnlySpan<byte> GetDiscriminatorPropertyName();

    protected abstract Type GetTypeForDiscriminator(IDiscriminatorStruct discriminator);

    protected abstract IDiscriminatorStruct GetDiscriminatorForType(Type type);

    public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        using var jsonDocument = JsonDocument.ParseValue(ref reader);

        var discriminatorPropertyName = GetDiscriminatorPropertyName();

        var discriminatorProperty = jsonDocument.RootElement
            .GetProperty(discriminatorPropertyName);

        IDiscriminatorStruct? typeDiscriminator = null;
        if (discriminatorProperty.ValueKind is JsonValueKind.String)
        {
            typeDiscriminator = new DiscriminatorStruct<string>(discriminatorProperty.GetString()!);
        }
        else if (discriminatorProperty.ValueKind is JsonValueKind.Number)
        {
            typeDiscriminator = new DiscriminatorStruct<int>(discriminatorProperty.GetInt32());
        }

        if (typeDiscriminator is null)
        {
            var discriminatorPropertyNameString = Encoding.UTF8.GetString(discriminatorPropertyName.ToArray());

            throw new JsonException($"Not found discriminator property '{discriminatorPropertyNameString}' for type {_polymorphicType}");
        }

        var type = GetTypeForDiscriminator(typeDiscriminator);

        return (T?) jsonDocument.Deserialize(type, options);
    }

    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
    {
        var instanceType = value.GetType();

        writer.WriteStartObject();

        var discriminatorPropertyName = GetDiscriminatorPropertyName();
        var discriminatorValue = GetDiscriminatorForType(instanceType);

        if (discriminatorValue is DiscriminatorStruct<string> discriminatorString)
        {
            writer.WriteString(discriminatorPropertyName, discriminatorString.Value);
        }
        else if (discriminatorValue is DiscriminatorStruct<int> discriminatorInt)
        {
            writer.WriteNumber(discriminatorPropertyName, discriminatorInt.Value);
        }

        var typeInfo = options.GetTypeInfo(instanceType);

        foreach (var p in typeInfo.Properties)
        {
            if (p.Get is null)
            {
                continue;
            }

            writer.WritePropertyName(p.Name);
            JsonSerializer.Serialize(writer, p.Get(value), p.PropertyType, options);
        }

        writer.WriteEndObject();
    }
}

#nullable enable

public partial class ProjectJsonSerializerContext
{
    public static System.Collections.Generic.IReadOnlyCollection<system.text.json.serialization.jsonconverter> GetPolymorphicConverters() =&gt; new System.Text.Json.Serialization.JsonConverter[]
    {
        new PolymorphicGlobalNamespace.PersonJsonPolymorphicConverter(),
    };

    public static void UsePolymorphicConverters(
        System.Collections.Generic.ICollection<system.text.json.serialization.jsonconverter> optionsConverters
    )
    {
        foreach (var converter in GetPolymorphicConverters())
        {
            optionsConverters.Add(converter);
        }
    }
}
// <auto-generated>

#nullable enable annotations
#nullable disable warnings

// Suppress warnings about [Obsolete] member usage in generated code.
#pragma warning disable CS0618
    
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "7.0.9.1816")]
    public partial class ProjectJsonSerializerContext
    {
        
        private static global::System.Text.Json.JsonSerializerOptions s_defaultOptions { get; } = new global::System.Text.Json.JsonSerializerOptions()
        {
            DefaultIgnoreCondition = global::System.Text.Json.Serialization.JsonIgnoreCondition.Never,
            IgnoreReadOnlyFields = false,
            IgnoreReadOnlyProperties = false,
            IncludeFields = false,
            WriteIndented = true,
                    PropertyNamingPolicy = global::System.Text.Json.JsonNamingPolicy.CamelCase
        };
        
        private static global::ProjectJsonSerializerContext? s_defaultContext;
        
        /// <summary>
        /// The default <see cref="global::System.Text.Json.Serialization.JsonSerializerContext"> associated with a default <see cref="global::System.Text.Json.JsonSerializerOptions"> instance.
        /// </see></see></summary>
        public static global::ProjectJsonSerializerContext Default =&gt; s_defaultContext ??= new global::ProjectJsonSerializerContext(new global::System.Text.Json.JsonSerializerOptions(s_defaultOptions));
        
        /// <summary>
        /// The source-generated options associated with this context.
        /// </summary>
        protected override global::System.Text.Json.JsonSerializerOptions? GeneratedSerializerOptions { get; } = s_defaultOptions;
        
        /// <inheritdoc>
        public ProjectJsonSerializerContext() : base(null)
        {
        }
        
        /// <inheritdoc>
        public ProjectJsonSerializerContext(global::System.Text.Json.JsonSerializerOptions options) : base(options)
        {
        }
        
        private static global::System.Text.Json.Serialization.JsonConverter? GetRuntimeProvidedCustomConverter(global::System.Text.Json.JsonSerializerOptions options, global::System.Type type)
        {
            global::System.Collections.Generic.IList<global::system.text.json.serialization.jsonconverter> converters = options.Converters;
        
            for (int i = 0; i &lt; converters.Count; i++)
            {
                global::System.Text.Json.Serialization.JsonConverter? converter = converters[i];
        
                if (converter.CanConvert(type))
                {
                    if (converter is global::System.Text.Json.Serialization.JsonConverterFactory factory)
                    {
                        converter = factory.CreateConverter(type, options);
                        if (converter == null || converter is global::System.Text.Json.Serialization.JsonConverterFactory)
                        {
                            throw new global::System.InvalidOperationException(string.Format("The converter '{0}' cannot return null or a JsonConverterFactory instance.", factory.GetType()));
                        }
                    }
        
                    return converter;
                }
            }
        
            return null;
        }
    }

// <auto-generated>

#nullable enable annotations
#nullable disable warnings

// Suppress warnings about [Obsolete] member usage in generated code.
#pragma warning disable CS0618
    public partial class ProjectJsonSerializerContext: global::System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver
    {
        /// <inheritdoc>
        public override global::System.Text.Json.Serialization.Metadata.JsonTypeInfo GetTypeInfo(global::System.Type type)
        {
            if (type == typeof(global::JsonPolymorphicGeneratorDemo.Person[]))
            {
                return this.PersonArray;
            }
        
            if (type == typeof(global::JsonPolymorphicGeneratorDemo.Person))
            {
                return this.Person;
            }
        
            if (type == typeof(global::System.String))
            {
                return this.String;
            }
        
            if (type == typeof(global::JsonPolymorphicGeneratorDemo.Student))
            {
                return this.Student;
            }
        
            if (type == typeof(global::JsonPolymorphicGeneratorDemo.Teacher))
            {
                return this.Teacher;
            }
        
            return null!;
        }
        
        global::System.Text.Json.Serialization.Metadata.JsonTypeInfo? global::System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver.GetTypeInfo(global::System.Type type, global::System.Text.Json.JsonSerializerOptions options)
        {
            if (type == typeof(global::JsonPolymorphicGeneratorDemo.Person[]))
            {
                return Create_PersonArray(options, makeReadOnly: false);
            }
        
            if (type == typeof(global::JsonPolymorphicGeneratorDemo.Person))
            {
                return Create_Person(options, makeReadOnly: false);
            }
        
            if (type == typeof(global::System.String))
            {
                return Create_String(options, makeReadOnly: false);
            }
        
            if (type == typeof(global::JsonPolymorphicGeneratorDemo.Student))
            {
                return Create_Student(options, makeReadOnly: false);
            }
        
            if (type == typeof(global::JsonPolymorphicGeneratorDemo.Teacher))
            {
                return Create_Teacher(options, makeReadOnly: false);
            }
        
            return null;
        }
        
    }

// <auto-generated>

#nullable enable annotations
#nullable disable warnings

// Suppress warnings about [Obsolete] member usage in generated code.
#pragma warning disable CS0618
    public partial class ProjectJsonSerializerContext
    {
        private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.person>? _Person;
        /// <summary>
        /// Defines the source generated JSON serialization contract metadata for a given type.
        /// </summary>
        public global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.person> Person
        {
            get =&gt; _Person ??= Create_Person(Options, makeReadOnly: true);
        }
        
        private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.person> Create_Person(global::System.Text.Json.JsonSerializerOptions options, bool makeReadOnly)
        {
            global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.person>? jsonTypeInfo = null;
            global::System.Text.Json.Serialization.JsonConverter? customConverter;
            if (options.Converters.Count &gt; 0 &amp;&amp; (customConverter = GetRuntimeProvidedCustomConverter(options, typeof(global::JsonPolymorphicGeneratorDemo.Person))) != null)
            {
                jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateValueInfo<global::jsonpolymorphicgeneratordemo.person>(options, customConverter);
            }
            else
            {
                global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues<global::jsonpolymorphicgeneratordemo.person> objectInfo = new global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues<global::jsonpolymorphicgeneratordemo.person>()
                {
                    ObjectCreator = null,
                    ObjectWithParameterizedConstructorCreator = null,
                    PropertyMetadataInitializer = _ =&gt; PersonPropInit(options),
                    ConstructorParameterMetadataInitializer = null,
                    NumberHandling = default,
                    SerializeHandler = PersonSerializeHandler
                };
        
                jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateObjectInfo<global::jsonpolymorphicgeneratordemo.person>(options, objectInfo);
            }
        
            if (makeReadOnly)
            {
                jsonTypeInfo.MakeReadOnly();
            }
        
            return jsonTypeInfo;
        }
        
        private static global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] PersonPropInit(global::System.Text.Json.JsonSerializerOptions options)
        {
            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] properties = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[1];
        
            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::system.string> info0 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::system.string>()
            {
                IsProperty = true,
                IsPublic = true,
                IsVirtual = false,
                DeclaringType = typeof(global::JsonPolymorphicGeneratorDemo.Person),
                Converter = null,
                Getter = static (obj) =&gt; ((global::JsonPolymorphicGeneratorDemo.Person)obj).Name!,
                Setter = static (obj, value) =&gt; ((global::JsonPolymorphicGeneratorDemo.Person)obj).Name = value!,
                IgnoreCondition = null,
                HasJsonInclude = false,
                IsExtensionData = false,
                NumberHandling = default,
                PropertyName = "Name",
                JsonPropertyName = null
            };
        
            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo propertyInfo0 = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::system.string>(options, info0);
            properties[0] = propertyInfo0;
        
            return properties;
        }
        
        // Intentionally not a static method because we create a delegate to it. Invoking delegates to instance
        // methods is almost as fast as virtual calls. Static methods need to go through a shuffle thunk.
        private void PersonSerializeHandler(global::System.Text.Json.Utf8JsonWriter writer, global::JsonPolymorphicGeneratorDemo.Person? value)
        {
            if (value == null)
            {
                writer.WriteNullValue();
                return;
            }
        
            writer.WriteStartObject();
            writer.WriteString(PropName_name, value.Name);
        
            writer.WriteEndObject();
        }
    }

// <auto-generated>

#nullable enable annotations
#nullable disable warnings

// Suppress warnings about [Obsolete] member usage in generated code.
#pragma warning disable CS0618
    public partial class ProjectJsonSerializerContext
    {
        private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.person  &#91;&#93;>? _PersonArray;
        /// <summary>
        /// Defines the source generated JSON serialization contract metadata for a given type.
        /// </summary>
        public global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.person  &#91;&#93;> PersonArray
        {
            get =&gt; _PersonArray ??= Create_PersonArray(Options, makeReadOnly: true);
        }
        
        private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.person  &#91;&#93;> Create_PersonArray(global::System.Text.Json.JsonSerializerOptions options, bool makeReadOnly)
        {
            global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.person  &#91;&#93;>? jsonTypeInfo = null;
            global::System.Text.Json.Serialization.JsonConverter? customConverter;
            if (options.Converters.Count &gt; 0 &amp;&amp; (customConverter = GetRuntimeProvidedCustomConverter(options, typeof(global::JsonPolymorphicGeneratorDemo.Person[]))) != null)
            {
                jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateValueInfo<global::jsonpolymorphicgeneratordemo.person  &#91;&#93;>(options, customConverter);
            }
            else
            {
                global::System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues<global::jsonpolymorphicgeneratordemo.person  &#91;&#93;> info = new global::System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues<global::jsonpolymorphicgeneratordemo.person  &#91;&#93;>()
                {
                    ObjectCreator = null,
                    NumberHandling = default,
                    SerializeHandler = PersonArraySerializeHandler
                };
        
                jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateArrayInfo<global::jsonpolymorphicgeneratordemo.person>(options, info);
        
            }
        
            if (makeReadOnly)
            {
                jsonTypeInfo.MakeReadOnly();
            }
        
            return jsonTypeInfo;
        }
        
        
        // Intentionally not a static method because we create a delegate to it. Invoking delegates to instance
        // methods is almost as fast as virtual calls. Static methods need to go through a shuffle thunk.
        private void PersonArraySerializeHandler(global::System.Text.Json.Utf8JsonWriter writer, global::JsonPolymorphicGeneratorDemo.Person[]? value)
        {
            if (value == null)
            {
                writer.WriteNullValue();
                return;
            }
        
            writer.WriteStartArray();
        
            for (int i = 0; i &lt; value.Length; i++)
            {
                PersonSerializeHandler(writer, value[i]!);
            }
        
            writer.WriteEndArray();
        }
    }

// <auto-generated>

#nullable enable annotations
#nullable disable warnings

// Suppress warnings about [Obsolete] member usage in generated code.
#pragma warning disable CS0618
    public partial class ProjectJsonSerializerContext
    {
        
        private static readonly global::System.Text.Json.JsonEncodedText PropName_name = global::System.Text.Json.JsonEncodedText.Encode("name");
    }

// <auto-generated>

#nullable enable annotations
#nullable disable warnings

// Suppress warnings about [Obsolete] member usage in generated code.
#pragma warning disable CS0618
    public partial class ProjectJsonSerializerContext
    {
        private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::system.string>? _String;
        /// <summary>
        /// Defines the source generated JSON serialization contract metadata for a given type.
        /// </summary>
        public global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::system.string> String
        {
            get =&gt; _String ??= Create_String(Options, makeReadOnly: true);
        }
        
        private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::system.string> Create_String(global::System.Text.Json.JsonSerializerOptions options, bool makeReadOnly)
        {
            global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::system.string>? jsonTypeInfo = null;
            global::System.Text.Json.Serialization.JsonConverter? customConverter;
            if (options.Converters.Count &gt; 0 &amp;&amp; (customConverter = GetRuntimeProvidedCustomConverter(options, typeof(global::System.String))) != null)
            {
                jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateValueInfo<global::system.string>(options, customConverter);
            }
            else
            {
                jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateValueInfo<global::system.string>(options, global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.StringConverter);
            }
        
            if (makeReadOnly)
            {
                jsonTypeInfo.MakeReadOnly();
            }
        
            return jsonTypeInfo;
        }
        
    }

// <auto-generated>

#nullable enable annotations
#nullable disable warnings

// Suppress warnings about [Obsolete] member usage in generated code.
#pragma warning disable CS0618
    public partial class ProjectJsonSerializerContext
    {
        private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.student>? _Student;
        /// <summary>
        /// Defines the source generated JSON serialization contract metadata for a given type.
        /// </summary>
        public global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.student> Student
        {
            get =&gt; _Student ??= Create_Student(Options, makeReadOnly: true);
        }
        
        private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.student> Create_Student(global::System.Text.Json.JsonSerializerOptions options, bool makeReadOnly)
        {
            global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.student>? jsonTypeInfo = null;
            global::System.Text.Json.Serialization.JsonConverter? customConverter;
            if (options.Converters.Count &gt; 0 &amp;&amp; (customConverter = GetRuntimeProvidedCustomConverter(options, typeof(global::JsonPolymorphicGeneratorDemo.Student))) != null)
            {
                jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateValueInfo<global::jsonpolymorphicgeneratordemo.student>(options, customConverter);
            }
            else
            {
                global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues<global::jsonpolymorphicgeneratordemo.student> objectInfo = new global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues<global::jsonpolymorphicgeneratordemo.student>()
                {
                    ObjectCreator = static () =&gt; new global::JsonPolymorphicGeneratorDemo.Student(),
                    ObjectWithParameterizedConstructorCreator = null,
                    PropertyMetadataInitializer = _ =&gt; StudentPropInit(options),
                    ConstructorParameterMetadataInitializer = null,
                    NumberHandling = default,
                    SerializeHandler = StudentSerializeHandler
                };
        
                jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateObjectInfo<global::jsonpolymorphicgeneratordemo.student>(options, objectInfo);
            }
        
            if (makeReadOnly)
            {
                jsonTypeInfo.MakeReadOnly();
            }
        
            return jsonTypeInfo;
        }
        
        private static global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] StudentPropInit(global::System.Text.Json.JsonSerializerOptions options)
        {
            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] properties = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[1];
        
            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::system.string> info0 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::system.string>()
            {
                IsProperty = true,
                IsPublic = true,
                IsVirtual = false,
                DeclaringType = typeof(global::JsonPolymorphicGeneratorDemo.Person),
                Converter = null,
                Getter = static (obj) =&gt; ((global::JsonPolymorphicGeneratorDemo.Person)obj).Name!,
                Setter = static (obj, value) =&gt; ((global::JsonPolymorphicGeneratorDemo.Person)obj).Name = value!,
                IgnoreCondition = null,
                HasJsonInclude = false,
                IsExtensionData = false,
                NumberHandling = default,
                PropertyName = "Name",
                JsonPropertyName = null
            };
        
            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo propertyInfo0 = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::system.string>(options, info0);
            properties[0] = propertyInfo0;
        
            return properties;
        }
        
        // Intentionally not a static method because we create a delegate to it. Invoking delegates to instance
        // methods is almost as fast as virtual calls. Static methods need to go through a shuffle thunk.
        private void StudentSerializeHandler(global::System.Text.Json.Utf8JsonWriter writer, global::JsonPolymorphicGeneratorDemo.Student? value)
        {
            if (value == null)
            {
                writer.WriteNullValue();
                return;
            }
        
            writer.WriteStartObject();
            writer.WriteString(PropName_name, value.Name);
        
            writer.WriteEndObject();
        }
    }

// <auto-generated>

#nullable enable annotations
#nullable disable warnings

// Suppress warnings about [Obsolete] member usage in generated code.
#pragma warning disable CS0618
    public partial class ProjectJsonSerializerContext
    {
        private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.teacher>? _Teacher;
        /// <summary>
        /// Defines the source generated JSON serialization contract metadata for a given type.
        /// </summary>
        public global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.teacher> Teacher
        {
            get =&gt; _Teacher ??= Create_Teacher(Options, makeReadOnly: true);
        }
        
        private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.teacher> Create_Teacher(global::System.Text.Json.JsonSerializerOptions options, bool makeReadOnly)
        {
            global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::jsonpolymorphicgeneratordemo.teacher>? jsonTypeInfo = null;
            global::System.Text.Json.Serialization.JsonConverter? customConverter;
            if (options.Converters.Count &gt; 0 &amp;&amp; (customConverter = GetRuntimeProvidedCustomConverter(options, typeof(global::JsonPolymorphicGeneratorDemo.Teacher))) != null)
            {
                jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateValueInfo<global::jsonpolymorphicgeneratordemo.teacher>(options, customConverter);
            }
            else
            {
                global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues<global::jsonpolymorphicgeneratordemo.teacher> objectInfo = new global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues<global::jsonpolymorphicgeneratordemo.teacher>()
                {
                    ObjectCreator = static () =&gt; new global::JsonPolymorphicGeneratorDemo.Teacher(),
                    ObjectWithParameterizedConstructorCreator = null,
                    PropertyMetadataInitializer = _ =&gt; TeacherPropInit(options),
                    ConstructorParameterMetadataInitializer = null,
                    NumberHandling = default,
                    SerializeHandler = TeacherSerializeHandler
                };
        
                jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateObjectInfo<global::jsonpolymorphicgeneratordemo.teacher>(options, objectInfo);
            }
        
            if (makeReadOnly)
            {
                jsonTypeInfo.MakeReadOnly();
            }
        
            return jsonTypeInfo;
        }
        
        private static global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] TeacherPropInit(global::System.Text.Json.JsonSerializerOptions options)
        {
            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] properties = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[1];
        
            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::system.string> info0 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::system.string>()
            {
                IsProperty = true,
                IsPublic = true,
                IsVirtual = false,
                DeclaringType = typeof(global::JsonPolymorphicGeneratorDemo.Person),
                Converter = null,
                Getter = static (obj) =&gt; ((global::JsonPolymorphicGeneratorDemo.Person)obj).Name!,
                Setter = static (obj, value) =&gt; ((global::JsonPolymorphicGeneratorDemo.Person)obj).Name = value!,
                IgnoreCondition = null,
                HasJsonInclude = false,
                IsExtensionData = false,
                NumberHandling = default,
                PropertyName = "Name",
                JsonPropertyName = null
            };
        
            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo propertyInfo0 = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::system.string>(options, info0);
            properties[0] = propertyInfo0;
        
            return properties;
        }
        
        // Intentionally not a static method because we create a delegate to it. Invoking delegates to instance
        // methods is almost as fast as virtual calls. Static methods need to go through a shuffle thunk.
        private void TeacherSerializeHandler(global::System.Text.Json.Utf8JsonWriter writer, global::JsonPolymorphicGeneratorDemo.Teacher? value)
        {
            if (value == null)
            {
                writer.WriteNullValue();
                return;
            }
        
            writer.WriteStartObject();
            writer.WriteString(PropName_name, value.Name);
        
            writer.WriteEndObject();
        }
    }

Code and pdf at

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

RSCG – N.SourceGenerators.UnionTypes

RSCG – N.SourceGenerators.UnionTypes
 
 

name N.SourceGenerators.UnionTypes
nuget https://www.nuget.org/packages/N.SourceGenerators.UnionTypes/
link https://github.com/Ne4to/N.SourceGenerators.UnionTypes
author Alexey Sosnin

Generating different union types

 

This is how you can use N.SourceGenerators.UnionTypes .

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="N.SourceGenerators.UnionTypes" Version="0.26.0" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using UnionTypesDemo;

Console.WriteLine("Save or not");
var data = SaveToDatabase.Save(0);
Console.WriteLine(data.IsValidationError);
data = SaveToDatabase.Save(1);
Console.WriteLine(data.IsSuccess);



using N.SourceGenerators.UnionTypes;
namespace UnionTypesDemo;
public record Success(int Value);
public record ValidationError(string Message);

[UnionType(typeof(Success))]
[UnionType(typeof(ValidationError))]
public partial class ResultSave
{
}





namespace UnionTypesDemo;

public class SaveToDatabase
{
    public static ResultSave Save(int i)
    {
        if(i ==0)
        {
            return new ValidationError(" cannot save 0");
        }
        return new Success(i);
    }
}




 

The code that is generated is

// <auto-generated>
//   This code was generated by https://github.com/Ne4to/N.SourceGenerators.UnionTypes
//   Feel free to open an issue
// </auto-generated>
#nullable enable
using System;
using System.Runtime.CompilerServices;

namespace N.SourceGenerators.UnionTypes
{
    [AttributeUsage(AttributeTargets.GenericParameter, Inherited = false, AllowMultiple = false)]
    internal sealed class GenericUnionTypeAttribute : Attribute
    {
        public string? Alias { get; set; }
        public bool AllowNull { get; set; }
        public object? TypeDiscriminator { get; set; }
    }
}
// <auto-generated>
//   This code was generated by https://github.com/Ne4to/N.SourceGenerators.UnionTypes
//   Feel free to open an issue
// </auto-generated>
#nullable enable
using System;
using System.Runtime.CompilerServices;

namespace N.SourceGenerators.UnionTypes
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
    internal sealed class JsonPolymorphicUnionAttribute : Attribute
    {
        public string? TypeDiscriminatorPropertyName { get; set; }
    }
}
// <auto-generated>
//   This code was generated by https://github.com/Ne4to/N.SourceGenerators.UnionTypes
//   Feel free to open an issue
// </auto-generated>
#pragma warning disable
#nullable enable
namespace UnionTypesDemo
{
    partial class ResultSave : System.IEquatable<ResultSave>
    {
        private readonly int _variantId;
        private const int SuccessId = 1;
        private readonly global::UnionTypesDemo.Success _success;
        public bool IsSuccess => _variantId == SuccessId;

        public global::UnionTypesDemo.Success AsSuccess
        {
            get
            {
                if (_variantId == SuccessId)
                    return _success;
                throw new System.InvalidOperationException($"Unable convert to Success. Inner value is {ValueAlias} not Success.");
            }
        }

        public ResultSave(global::UnionTypesDemo.Success success)
        {
            System.ArgumentNullException.ThrowIfNull(success);
            _variantId = SuccessId;
            _success = success;
        }

        public static implicit operator ResultSave(global::UnionTypesDemo.Success success) => new ResultSave(success);
        public static explicit operator global::UnionTypesDemo.Success(ResultSave value)
        {
            if (value._variantId == SuccessId)
                return value._success;
            throw new System.InvalidOperationException($"Unable convert to Success. Inner value is {value.ValueAlias} not Success.");
        }

        public bool TryGetSuccess([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::UnionTypesDemo.Success value)
        {
            if (_variantId == SuccessId)
            {
                value = _success;
                return true;
            }
            else
            {
                value = default;
                return false;
            }
        }

        private const int ValidationErrorId = 2;
        private readonly global::UnionTypesDemo.ValidationError _validationError;
        public bool IsValidationError => _variantId == ValidationErrorId;

        public global::UnionTypesDemo.ValidationError AsValidationError
        {
            get
            {
                if (_variantId == ValidationErrorId)
                    return _validationError;
                throw new System.InvalidOperationException($"Unable convert to ValidationError. Inner value is {ValueAlias} not ValidationError.");
            }
        }

        public ResultSave(global::UnionTypesDemo.ValidationError validationError)
        {
            System.ArgumentNullException.ThrowIfNull(validationError);
            _variantId = ValidationErrorId;
            _validationError = validationError;
        }

        public static implicit operator ResultSave(global::UnionTypesDemo.ValidationError validationError) => new ResultSave(validationError);
        public static explicit operator global::UnionTypesDemo.ValidationError(ResultSave value)
        {
            if (value._variantId == ValidationErrorId)
                return value._validationError;
            throw new System.InvalidOperationException($"Unable convert to ValidationError. Inner value is {value.ValueAlias} not ValidationError.");
        }

        public bool TryGetValidationError([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::UnionTypesDemo.ValidationError value)
        {
            if (_variantId == ValidationErrorId)
            {
                value = _validationError;
                return true;
            }
            else
            {
                value = default;
                return false;
            }
        }

        public TOut Match<TOut>(global::System.Func<global::UnionTypesDemo.Success, TOut> matchSuccess, global::System.Func<global::UnionTypesDemo.ValidationError, TOut> matchValidationError)
        {
            if (_variantId == SuccessId)
                return matchSuccess(_success);
            if (_variantId == ValidationErrorId)
                return matchValidationError(_validationError);
            throw new System.InvalidOperationException("Inner type is unknown");
        }

        public async global::System.Threading.Tasks.Task<TOut> MatchAsync<TOut>(global::System.Func<global::UnionTypesDemo.Success, global::System.Threading.CancellationToken, global::System.Threading.Tasks.Task<TOut>> matchSuccess, global::System.Func<global::UnionTypesDemo.ValidationError, global::System.Threading.CancellationToken, global::System.Threading.Tasks.Task<TOut>> matchValidationError, global::System.Threading.CancellationToken ct)
        {
            if (_variantId == SuccessId)
                return await matchSuccess(_success, ct).ConfigureAwait(false);
            if (_variantId == ValidationErrorId)
                return await matchValidationError(_validationError, ct).ConfigureAwait(false);
            throw new System.InvalidOperationException("Inner type is unknown");
        }

        public void Switch(global::System.Action<global::UnionTypesDemo.Success> switchSuccess, global::System.Action<global::UnionTypesDemo.ValidationError> switchValidationError)
        {
            if (_variantId == SuccessId)
            {
                switchSuccess(_success);
                return;
            }

            if (_variantId == ValidationErrorId)
            {
                switchValidationError(_validationError);
                return;
            }

            throw new System.InvalidOperationException("Inner type is unknown");
        }

        public async global::System.Threading.Tasks.Task SwitchAsync(global::System.Func<global::UnionTypesDemo.Success, global::System.Threading.CancellationToken, global::System.Threading.Tasks.Task> switchSuccess, global::System.Func<global::UnionTypesDemo.ValidationError, global::System.Threading.CancellationToken, global::System.Threading.Tasks.Task> switchValidationError, global::System.Threading.CancellationToken ct)
        {
            if (_variantId == SuccessId)
            {
                await switchSuccess(_success, ct).ConfigureAwait(false);
                return;
            }

            if (_variantId == ValidationErrorId)
            {
                await switchValidationError(_validationError, ct).ConfigureAwait(false);
                return;
            }

            throw new System.InvalidOperationException("Inner type is unknown");
        }

        public global::System.Type ValueType
        {
            get
            {
                if (_variantId == SuccessId)
                    return typeof(global::UnionTypesDemo.Success);
                if (_variantId == ValidationErrorId)
                    return typeof(global::UnionTypesDemo.ValidationError);
                throw new System.InvalidOperationException("Inner type is unknown");
            }
        }

        private string ValueAlias
        {
            get
            {
                if (_variantId == SuccessId)
                    return "Success";
                if (_variantId == ValidationErrorId)
                    return "ValidationError";
                throw new System.InvalidOperationException("Inner type is unknown");
            }
        }

        public override int GetHashCode()
        {
            if (_variantId == SuccessId)
                return _success.GetHashCode();
            if (_variantId == ValidationErrorId)
                return _validationError.GetHashCode();
            throw new System.InvalidOperationException("Inner type is unknown");
        }

        public static bool operator ==(ResultSave? left, ResultSave? right)
        {
            return Equals(left, right);
        }

        public static bool operator !=(ResultSave? left, ResultSave? right)
        {
            return !Equals(left, right);
        }

        public bool Equals(ResultSave? other)
        {
            if (ReferenceEquals(null, other))
            {
                return false;
            }

            if (ReferenceEquals(this, other))
            {
                return true;
            }

            if (ValueType != other.ValueType)
            {
                return false;
            }

            if (_variantId == SuccessId)
                return System.Collections.Generic.EqualityComparer<global::UnionTypesDemo.Success>.Default.Equals(_success, other._success);
            if (_variantId == ValidationErrorId)
                return System.Collections.Generic.EqualityComparer<global::UnionTypesDemo.ValidationError>.Default.Equals(_validationError, other._validationError);
            throw new System.InvalidOperationException("Inner type is unknown");
        }

        public override string ToString()
        {
            if (_variantId == SuccessId)
                return _success.ToString();
            if (_variantId == ValidationErrorId)
                return _validationError.ToString();
            throw new System.InvalidOperationException("Inner type is unknown");
        }

        public override bool Equals(object? other)
        {
            if (ReferenceEquals(null, other))
            {
                return false;
            }

            if (ReferenceEquals(this, other))
            {
                return true;
            }

            if (other.GetType() != typeof(ResultSave))
            {
                return false;
            }

            return Equals((ResultSave)other);
        }
    }
}
#nullable enable
using System;
using System.Runtime.CompilerServices;

namespace N.SourceGenerators.UnionTypes
{
    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
    sealed class UnionConverterAttribute : Attribute
    {
        public Type FromType { get; }
        public Type ToType { get; }
        public string? MethodName { get; }

        public UnionConverterAttribute(Type fromType, Type toType, string? methodName = null)
        {
            FromType = fromType;
            ToType = toType;
            MethodName = methodName;
        }
    }
}
// <auto-generated>
//   This code was generated by https://github.com/Ne4to/N.SourceGenerators.UnionTypes
//   Feel free to open an issue
// </auto-generated>
#nullable enable
using System;
using System.Runtime.CompilerServices;

namespace N.SourceGenerators.UnionTypes
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = true)]
    sealed class UnionConverterFromAttribute : Attribute
    {
        public Type FromType { get; }

        public UnionConverterFromAttribute(Type fromType)
        {
            FromType = fromType;
        }
    }
}
// <auto-generated>
//   This code was generated by https://github.com/Ne4to/N.SourceGenerators.UnionTypes
//   Feel free to open an issue
// </auto-generated>
#nullable enable
using System;
using System.Runtime.CompilerServices;

namespace N.SourceGenerators.UnionTypes
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = true)]
    sealed class UnionConverterToAttribute : Attribute
    {
        public Type ToType { get; }

        public UnionConverterToAttribute(Type toType)
        {
            ToType = toType;
        }
    }
}
// <auto-generated>
//   This code was generated by https://github.com/Ne4to/N.SourceGenerators.UnionTypes
//   Feel free to open an issue
// </auto-generated>
#nullable enable
using System;
using System.Runtime.CompilerServices;

namespace N.SourceGenerators.UnionTypes
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = true)]
    internal sealed class UnionTypeAttribute : Attribute
    {
        public Type Type { get; }
        public string? Alias { get; }
        public int Order { get; }
        public bool AllowNull { get; set; }
        public object? TypeDiscriminator { get; set; }

        public UnionTypeAttribute(Type type, string? alias = null, [CallerLineNumber] int order = 0)
        {
            Type = type;
            Alias = alias;
            Order = order;
        }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/N.SourceGenerators.UnionTypes

RSCG – AutoConstructor

RSCG – AutoConstructor
 
 

name AutoConstructor
nuget https://www.nuget.org/packages/AutoConstructor/
link https://github.com/k94ll13nn3/AutoConstructor
author Kévin Gallienne

Generating constructor for class with many properties

 

This is how you can use AutoConstructor .

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>

	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

	<ItemGroup>
	  <PackageReference Include="AutoConstructor" Version="4.1.1">
	    <PrivateAssets>all</PrivateAssets>
	    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
	  </PackageReference>
	</ItemGroup>
</Project>


The code that you will use is


using QuickConstructorDemo;

var p = new Person("Andrei", "Ignat");

Console.WriteLine(p.FullName());


namespace QuickConstructorDemo;

[AutoConstructor]
internal partial class Person
{
    private readonly string FirstName;
    private readonly string? LastName;
    
    public string FullName() => $"{FirstName} {LastName}";
    
}


 

The code that is generated is

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by the AutoConstructor source generator.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

[System.AttributeUsage(System.AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
internal sealed class AutoConstructorAttribute : System.Attribute
{
}

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by the AutoConstructor source generator.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

[System.AttributeUsage(System.AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
internal sealed class AutoConstructorIgnoreAttribute : System.Attribute
{
}

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by the AutoConstructor source generator.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

[System.AttributeUsage(System.AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
internal sealed class AutoConstructorInjectAttribute : System.Attribute
{
    public AutoConstructorInjectAttribute(string initializer = null, string parameterName = null, System.Type injectedType = null)
    {
        Initializer = initializer;
        ParameterName = parameterName;
        InjectedType = injectedType;
    }

    public string Initializer { get; }

    public string ParameterName { get; }

    public System.Type InjectedType { get; }
}

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by the AutoConstructor source generator.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
#nullable enable
namespace QuickConstructorDemo
{
    partial class Person
    {
        public Person(string FirstName, string? LastName)
        {
            this.FirstName = FirstName;
            this.LastName = LastName;
        }
    }
}

Code and pdf at

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

RSCG – DudNet

RSCG – DudNet
 
 

name DudNet
nuget https://www.nuget.org/packages/Jwshyns.DudNet/
link https://github.com/jwshyns/DudNet
author jwshyns

Generate proxy classes for the principal classes

 

This is how you can use DudNet .

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="Jwshyns.DudNet" Version="1.2.0" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using DudNetDemo;

var p = new Person();
var p1= new PersonProxy(p);
p1.FirstName = "John";
p1.LastName = "Doe";
Console.WriteLine(p.FullName());
Console.WriteLine(p1.FullName());



using DudNet.Attributes;
using System.Runtime.CompilerServices;

namespace DudNetDemo;
[ProxyService]
public partial class Person : IPerson
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public string FullName()
    {
        return FirstName + " " + LastName;
    }
}


 

The code that is generated is

using System.Runtime.CompilerServices;
using DudNet.Attributes;
using System.Runtime.CompilerServices;

namespace DudNetDemo;

/// <inheritdoc cref="IPerson"/>
public partial class PersonDud : IPerson {

	public string? FirstName {
		get {
			return (string?) default;
		}
		set {
		}
	}
	public string? LastName {
		get {
			return (string?) default;
		}
		set {
		}
	}
	public string FullName() {
		return (string) default;
	}

}
using System.Runtime.CompilerServices;
using DudNet.Attributes;
using System.Runtime.CompilerServices;

namespace DudNetDemo;

/// <inheritdoc cref="IPerson"/>
public partial class PersonProxy : IPerson {

	private readonly IPerson _service;

	public string? FirstName {
		get {
			Interceptor();
			get_FirstNameInterceptor();
			return _service.FirstName;
		}
		set {
			Interceptor();
			set_FirstNameInterceptor(value);
			_service.FirstName = value;
		}
	}
	public string? LastName {
		get {
			Interceptor();
			get_LastNameInterceptor();
			return _service.LastName;
		}
		set {
			Interceptor();
			set_LastNameInterceptor(value);
			_service.LastName = value;
		}
	}
	public string FullName() {
		Interceptor();
		FullNameInterceptor();
		return _service.FullName();
	}
	partial void Interceptor([CallerMemberName]string callerName = null);
	partial void get_FirstNameInterceptor();
	partial void set_FirstNameInterceptor(string? value);
	partial void get_LastNameInterceptor();
	partial void set_LastNameInterceptor(string? value);
	partial void FullNameInterceptor();

}

Code and pdf at

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

RSCG – DynamicsMapper

RSCG – DynamicsMapper
 
 

name DynamicsMapper
nuget https://www.nuget.org/packages/YC.DynamicsMapper/
link https://github.com/YonatanCohavi/DynamicsMapper/
author Yonatan Cohavi

Mapper for Dataverse client – generates also column names from properties

 

This is how you can use DynamicsMapper .

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>

	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

	<ItemGroup>
	  <PackageReference Include="Microsoft.PowerPlatform.Dataverse.Client" Version="1.1.14" />
	  <PackageReference Include="YC.DynamicsMapper" Version="1.0.8" />
	</ItemGroup>
</Project>


The code that you will use is


using NextGenMapperDemo;
var pm = new PersonMapper();
Console.WriteLine(pm.Entityname);



using DynamicsMapper.Abstractions;

namespace NextGenMapperDemo;


[CrmEntity("person")]
public class Person
{
    [CrmField("personid", Mapping = MappingType.PrimaryId)]
    public Guid ID { get; set; }
    [CrmField("name")]

    public string? Name { get; set; }
}

 

The code that is generated is

// <auto-generated />
#nullable enable
using Microsoft.Xrm.Sdk;
using System.Linq;

namespace DynamicsMapper.Extension
{
    public static class EntityExtension
    {
        public static Entity? GetAliasedEntity(this Entity entity, string alias)
        {
            var attributes = entity.Attributes.Where(e => e.Key.StartsWith(alias)).ToArray();
            if (!attributes.Any())
                return null;
            var aliasEntity = new Entity();
            foreach (var attribute in attributes)
            {
                if (!(attribute.Value is AliasedValue aliasedValued))
                    continue;
                if (string.IsNullOrEmpty(aliasEntity.LogicalName))
                    aliasEntity.LogicalName = aliasedValued.EntityLogicalName;
                aliasEntity[aliasedValued.AttributeLogicalName] = aliasedValued.Value;
            }

            return aliasEntity;
        }
    }
}
// <auto-generated />
#nullable enable
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;

namespace DynamicsMapper.Mappers
{
    public interface IEntityMapper<T>
    {
        public string Entityname { get; }
        public ColumnSet Columns { get; }

        public T Map(Entity entity);
        public T? Map(Entity entity, string alias);
        public Entity Map(T model);
    }
}
// <auto-generated />
#nullable enable
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using DynamicsMapper.Extension;
using DynamicsMapper.Mappers;
using System;

namespace NextGenMapperDemo
{
    public class PersonMapper : IEntityMapper<Person>
    {
        private static readonly string[] columns = new[]
        {
            "personid",
            "name"
        };
        public ColumnSet Columns => new ColumnSet(columns);

        private const string entityname = "person";
        public string Entityname => entityname;

        public Entity Map(Person person)
        {
            var entity = new Entity(entityname);
            entity.Id = person.ID;
            entity["name"] = person.Name;
            return entity;
        }

        public Person? Map(Entity entity, string alias) => InternalMap(entity, alias);
        public Person Map(Entity entity) => InternalMap(entity)!;
        private static Person? InternalMap(Entity source, string? alias = null)
        {
            Entity? entity;
            if (string.IsNullOrEmpty(alias))
            {
                entity = source;
            }
            else
            {
                entity = source.GetAliasedEntity(alias);
                if (entity is null)
                    return null;
            }

            if (entity?.LogicalName != entityname)
                throw new ArgumentException($"entity LogicalName expected to be {entityname} recived: {entity?.LogicalName}", "entity");
            var person = new Person();
            person.ID = entity.GetAttributeValue<Guid>("personid");
            person.Name = entity.GetAttributeValue<string?>("name");
            return person;
        }
    }
}

Code and pdf at

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

RSCG – UnitGenerator

RSCG – UnitGenerator
 
 

name UnitGenerator
nuget https://www.nuget.org/packages/UnitGenerator/
link https://github.com/Cysharp/UnitGenerator
author Cysharp, Inc

Generating classes instead of value objects( e.g. int)

 

This is how you can use UnitGenerator .

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>

  <PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

  <ItemGroup>
    <PackageReference Include="UnitGenerator" Version="1.5.1">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>
</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 UnitGenerator;

namespace StronglyDemo;


[UnitOf(typeof(int))]
public partial struct YearId { }

[UnitOf(typeof(int))]
public partial struct MonthId { }

[UnitOf(typeof(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.AsPrimitive(), monthId.AsPrimitive(), dayId.AsPrimitive());
    }
}


 

The code that is generated is

// <auto-generated>
// THIS (.cs) FILE IS GENERATED BY UnitGenerator. DO NOT CHANGE IT.
// </auto-generated>
#pragma warning disable CS8669
using System;
using System.Globalization;
#if NET7_0_OR_GREATER
using System.Numerics;
#endif
namespace StronglyDemo
{
    [System.ComponentModel.TypeConverter(typeof(DayIdTypeConverter))]
    readonly partial struct DayId 
        : IEquatable<DayId>
#if NET7_0_OR_GREATER
        , IEqualityOperators<DayId, DayId, bool>
#endif    
    {
        readonly int value;

        public int AsPrimitive() => value;

        public DayId(int value)
        {
            this.value = value;
        }
        
        public static explicit operator int(DayId value)
        {
            return value.value;
        }

        public static explicit operator DayId(int value)
        {
            return new DayId(value);
        }

        public bool Equals(DayId other)
        {
            return value.Equals(other.value);
        }

        public override bool Equals(object obj)
        {
            if (obj == null) return false;
            var t = obj.GetType();
            if (t == typeof(DayId))
            {
                return Equals((DayId)obj);
            }
            if (t == typeof(int))
            {
                return value.Equals((int)obj);
            }

            return value.Equals(obj);
        }
        
        public static bool operator ==(DayId x, DayId y)
        {
            return x.value.Equals(y.value);
        }

        public static bool operator !=(DayId x, DayId y)
        {
            return !x.value.Equals(y.value);
        }

        public override int GetHashCode()
        {
            return value.GetHashCode();
        }

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

        // Default
        
        private class DayIdTypeConverter : System.ComponentModel.TypeConverter
        {
            private static readonly Type WrapperType = typeof(DayId);
            private static readonly Type ValueType = typeof(int);

            public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, Type sourceType)
            {
                if (sourceType == WrapperType || sourceType == ValueType)
                {
                    return true;
                }

                return base.CanConvertFrom(context, sourceType);
            }

            public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, Type destinationType)
            {
                if (destinationType == WrapperType || destinationType == ValueType)
                {
                    return true;
                }

                return base.CanConvertTo(context, destinationType);
            }

            public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
            {
                if (value != null)
                {
                    var t = value.GetType();
                    if (t == typeof(DayId))
                    {
                        return (DayId)value;
                    }
                    if (t == typeof(int))
                    {
                        return new DayId((int)value);
                    }
                }

                return base.ConvertFrom(context, culture, value);
            }

            public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
            {
                if (value is DayId wrappedValue)
                {
                    if (destinationType == WrapperType)
                    {
                        return wrappedValue;
                    }

                    if (destinationType == ValueType)
                    {
                        return wrappedValue.AsPrimitive();
                    }
                }

                return base.ConvertTo(context, culture, value, destinationType);
            }
        }
    }
}

// <auto-generated>
// THIS (.cs) FILE IS GENERATED BY UnitGenerator. DO NOT CHANGE IT.
// </auto-generated>
#pragma warning disable CS8669
using System;
using System.Globalization;
#if NET7_0_OR_GREATER
using System.Numerics;
#endif
namespace StronglyDemo
{
    [System.ComponentModel.TypeConverter(typeof(MonthIdTypeConverter))]
    readonly partial struct MonthId 
        : IEquatable<MonthId>
#if NET7_0_OR_GREATER
        , IEqualityOperators<MonthId, MonthId, bool>
#endif    
    {
        readonly int value;

        public int AsPrimitive() => value;

        public MonthId(int value)
        {
            this.value = value;
        }
        
        public static explicit operator int(MonthId value)
        {
            return value.value;
        }

        public static explicit operator MonthId(int value)
        {
            return new MonthId(value);
        }

        public bool Equals(MonthId other)
        {
            return value.Equals(other.value);
        }

        public override bool Equals(object obj)
        {
            if (obj == null) return false;
            var t = obj.GetType();
            if (t == typeof(MonthId))
            {
                return Equals((MonthId)obj);
            }
            if (t == typeof(int))
            {
                return value.Equals((int)obj);
            }

            return value.Equals(obj);
        }
        
        public static bool operator ==(MonthId x, MonthId y)
        {
            return x.value.Equals(y.value);
        }

        public static bool operator !=(MonthId x, MonthId y)
        {
            return !x.value.Equals(y.value);
        }

        public override int GetHashCode()
        {
            return value.GetHashCode();
        }

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

        // Default
        
        private class MonthIdTypeConverter : System.ComponentModel.TypeConverter
        {
            private static readonly Type WrapperType = typeof(MonthId);
            private static readonly Type ValueType = typeof(int);

            public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, Type sourceType)
            {
                if (sourceType == WrapperType || sourceType == ValueType)
                {
                    return true;
                }

                return base.CanConvertFrom(context, sourceType);
            }

            public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, Type destinationType)
            {
                if (destinationType == WrapperType || destinationType == ValueType)
                {
                    return true;
                }

                return base.CanConvertTo(context, destinationType);
            }

            public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
            {
                if (value != null)
                {
                    var t = value.GetType();
                    if (t == typeof(MonthId))
                    {
                        return (MonthId)value;
                    }
                    if (t == typeof(int))
                    {
                        return new MonthId((int)value);
                    }
                }

                return base.ConvertFrom(context, culture, value);
            }

            public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
            {
                if (value is MonthId wrappedValue)
                {
                    if (destinationType == WrapperType)
                    {
                        return wrappedValue;
                    }

                    if (destinationType == ValueType)
                    {
                        return wrappedValue.AsPrimitive();
                    }
                }

                return base.ConvertTo(context, culture, value, destinationType);
            }
        }
    }
}

// <auto-generated>
// THIS (.cs) FILE IS GENERATED BY UnitGenerator. DO NOT CHANGE IT.
// </auto-generated>
#pragma warning disable CS8669
using System;
using System.Globalization;
#if NET7_0_OR_GREATER
using System.Numerics;
#endif
namespace StronglyDemo
{
    [System.ComponentModel.TypeConverter(typeof(YearIdTypeConverter))]
    readonly partial struct YearId 
        : IEquatable<YearId>
#if NET7_0_OR_GREATER
        , IEqualityOperators<YearId, YearId, bool>
#endif    
    {
        readonly int value;

        public int AsPrimitive() => value;

        public YearId(int value)
        {
            this.value = value;
        }
        
        public static explicit operator int(YearId value)
        {
            return value.value;
        }

        public static explicit operator YearId(int value)
        {
            return new YearId(value);
        }

        public bool Equals(YearId other)
        {
            return value.Equals(other.value);
        }

        public override bool Equals(object obj)
        {
            if (obj == null) return false;
            var t = obj.GetType();
            if (t == typeof(YearId))
            {
                return Equals((YearId)obj);
            }
            if (t == typeof(int))
            {
                return value.Equals((int)obj);
            }

            return value.Equals(obj);
        }
        
        public static bool operator ==(YearId x, YearId y)
        {
            return x.value.Equals(y.value);
        }

        public static bool operator !=(YearId x, YearId y)
        {
            return !x.value.Equals(y.value);
        }

        public override int GetHashCode()
        {
            return value.GetHashCode();
        }

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

        // Default
        
        private class YearIdTypeConverter : System.ComponentModel.TypeConverter
        {
            private static readonly Type WrapperType = typeof(YearId);
            private static readonly Type ValueType = typeof(int);

            public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, Type sourceType)
            {
                if (sourceType == WrapperType || sourceType == ValueType)
                {
                    return true;
                }

                return base.CanConvertFrom(context, sourceType);
            }

            public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, Type destinationType)
            {
                if (destinationType == WrapperType || destinationType == ValueType)
                {
                    return true;
                }

                return base.CanConvertTo(context, destinationType);
            }

            public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
            {
                if (value != null)
                {
                    var t = value.GetType();
                    if (t == typeof(YearId))
                    {
                        return (YearId)value;
                    }
                    if (t == typeof(int))
                    {
                        return new YearId((int)value);
                    }
                }

                return base.ConvertFrom(context, culture, value);
            }

            public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
            {
                if (value is YearId wrappedValue)
                {
                    if (destinationType == WrapperType)
                    {
                        return wrappedValue;
                    }

                    if (destinationType == ValueType)
                    {
                        return wrappedValue.AsPrimitive();
                    }
                }

                return base.ConvertTo(context, culture, value, destinationType);
            }
        }
    }
}

// <auto-generated>
// THIS (.cs) FILE IS GENERATED BY UnitGenerator. DO NOT CHANGE IT.
// </auto-generated>
#pragma warning disable CS8669
#pragma warning disable CS8625
using System;
#if NET7_0_OR_GREATER
using System.Numerics;
#endif

namespace UnitGenerator
{
    [AttributeUsage(AttributeTargets.Struct, AllowMultiple = false)]
    internal class UnitOfAttribute : Attribute
    {
        public Type Type { get; }
        public UnitGenerateOptions Options { get; }
        public UnitArithmeticOperators ArithmeticOperators { get; set; } = UnitArithmeticOperators.All;
        public string ToStringFormat { get; set; }

        public UnitOfAttribute(Type type, UnitGenerateOptions options = UnitGenerateOptions.None)
        {
            this.Type = type;
            this.Options = options;
        }
    }
    
    [Flags]
    internal enum UnitGenerateOptions
    {
        None = 0,
        ImplicitOperator = 1,
        ParseMethod = 1 << 1,
        MinMaxMethod = 1 << 2,
        ArithmeticOperator = 1 << 3,
        ValueArithmeticOperator = 1 << 4,
        Comparable = 1 << 5,
        Validate = 1 << 6,
        JsonConverter = 1 << 7,
        MessagePackFormatter = 1 << 8,
        DapperTypeHandler = 1 << 9,
        EntityFrameworkValueConverter = 1 << 10,
        WithoutComparisonOperator = 1 << 11,
        JsonConverterDictionaryKeySupport = 1 << 12,
        Normalize = 1 << 13,
    }

    [Flags]
    internal enum UnitArithmeticOperators
    {
        All = Addition | Subtraction | Multiply | Division | Increment | Decrement,
        Addition = 1,
        Subtraction = 1 << 1,
        Multiply = 1 << 2,
        Division = 1 << 3,
        Increment = 1 << 4,
        Decrement = 1 << 5,
    }
}

Code and pdf at

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

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.