Category: roslyn

RSCG – CopyCat

RSCG – CopyCat
 
 

name CopyCat
nuget https://www.nuget.org/packages/Copycat/
link https://github.com/Otaman/Copycat/
author Serhii Buta

Implementation of the Decorator pattern in C# – only for not implemented methods

 

This is how you can use CopyCat .

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="Copycat" Version="0.2.0-beta.1" OutputItemType="Analyzer"   />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using CCDemo;

ICoffee c =new Coffee();
c= new CoffeeWithLogging(c);
await c.Prepare();



namespace CCDemo;

internal interface ICoffee
{
    //for the moment does not work for properties in interface
    //string? Name { get; set; }
    Task<bool> Prepare();

    string[] GetIngredients();
}


namespace CCDemo;
internal class Coffee : ICoffee
{
    public string? Name { get; set; }
    public async Task<bool> Prepare()
    {
        Console.WriteLine("start prepare coffee");
        await Task.Delay(1000);
        Console.WriteLine("finish prepare coffee");
        return true;
    }
    public string[] GetIngredients() => new[] { "water", "coffee" };

}



using Copycat;

namespace CCDemo;
[Decorate]
internal partial class CoffeeWithLogging: ICoffee
{
    [Template]
    private string[] AddLogging(Func<string[]> action)
    {
        try
        {
            Console.WriteLine($"start logging {nameof(action)}  ");
            return action();
        }
        catch (Exception e)
        {
            Console.WriteLine($"exception  {nameof(action)} ");
            throw;
        }
        finally
        {
               Console.WriteLine($"end logging {nameof(action)} ");
        }
    }


    [Template]
    public async Task<bool> AddLogging(Func<Task<bool>> action)       
    {
        try
        {
            Console.WriteLine($"start logging {nameof(action)} ");
            return await action();
        }
        catch (Exception e)
        {
            Console.WriteLine($"exception  {nameof(action)} ");
            throw;
        }
        finally
        {
            Console.WriteLine($"end logging {nameof(action)} ");
        }
    }
}


 

The code that is generated is

// <auto-generated/>
using Copycat;

namespace CCDemo;
internal partial class CoffeeWithLogging
{
    private CCDemo.ICoffee _decorated;
    public CoffeeWithLogging(CCDemo.ICoffee decorated)
    {
        _decorated = decorated;
    }

    /// <see cref = "CoffeeWithLogging.AddLogging(Func{Task{bool}})"/>
    public async //for the moment does not work for properties in interface
    //string? Name { get; set; }
    Task<bool> Prepare()
    {
        try
        {
            Console.WriteLine($"start logging {nameof(Prepare)} ");
            return await _decorated.Prepare();
        }
        catch (Exception e)
        {
            Console.WriteLine($"exception  {nameof(Prepare)} ");
            throw;
        }
        finally
        {
            Console.WriteLine($"end logging {nameof(Prepare)} ");
        }
    }

    /// <see cref = "CoffeeWithLogging.AddLogging(Func{string[]})"/>
    public string[] GetIngredients()
    {
        try
        {
            Console.WriteLine($"start logging {nameof(GetIngredients)}  ");
            return _decorated.GetIngredients();
        }
        catch (Exception e)
        {
            Console.WriteLine($"exception  {nameof(GetIngredients)} ");
            throw;
        }
        finally
        {
            Console.WriteLine($"end logging {nameof(GetIngredients)} ");
        }
    }
}

Code and pdf at

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

RSCG – AspectGenerator

RSCG – AspectGenerator
 
 

name AspectGenerator
nuget https://www.nuget.org/packages/AspectGenerator/
link https://github.com/igor-tkachev/AspectGenerator
author Igor Tkachev

AOP for methods in the same project. Uses interceptors

 

This is how you can use AspectGenerator .

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="AspectGenerator" Version="0.0.9-preview" OutputItemType="Analyzer"  />
  </ItemGroup>
<PropertyGroup>
    
    <InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);AspectGenerator</InterceptorsPreviewNamespaces>

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


The code that you will use is


using AG;

var p=new Person { FirstName="Ignat", LastName="Andrei" };
var x= p.FullName();
Console.WriteLine(x);   


namespace AG;

internal class Person
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    [Metrics]
    public string FullName()
    {
        return $"{FirstName} {LastName}";
    }
}


using System.Diagnostics;
using AspectGenerator;
namespace AG;

[Aspect(
       // Specify the name of the method used in the 'using' statement
       // that returns an IDisposable object.
       OnUsing = nameof(OnUsing)
       )]
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
sealed class MetricsAttribute : Attribute
{
    //static readonly ActivitySource _activitySource = new("Sample.Aspect");

    public static Activity? OnUsing(InterceptInfo info)
    {
        Console.WriteLine($"Entering {info.MemberInfo.Name}");
        return null;
        //var data=_activitySource.StartActivity(info.MemberInfo.Name);
        //return data;
    }
}

 

The code that is generated is

// <auto-generated/>
#pragma warning disable
#nullable enable

using System;

#if AG_GENERATE_API || !AG_NOT_GENERATE_API

namespace AspectGenerator
{
	/// <summary>
	/// <para>Defines an aspect.</para>
	/// <para>Create a new attribute decorated with this attribute to define an aspect.</para>
	/// </summary>
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
#if AG_PUBLIC_API
	public
#endif
	sealed class AspectAttribute : Attribute
	{
		public string?   OnInit            { get; set; }
		public string?   OnUsing           { get; set; }
		public string?   OnUsingAsync      { get; set; }
		public string?   OnBeforeCall      { get; set; }
		public string?   OnBeforeCallAsync { get; set; }
		public string?   OnCall            { get; set; }
		public string?   OnAfterCall       { get; set; }
		public string?   OnAfterCallAsync  { get; set; }
		public string?   OnCatch           { get; set; }
		public string?   OnCatchAsync      { get; set; }
		public string?   OnFinally         { get; set; }
		public string?   OnFinallyAsync    { get; set; }
		public string[]? InterceptMethods  { get; set; }
		public bool      UseInterceptType  { get; set; }
		public bool      PassArguments     { get; set; }
		public bool      UseInterceptData  { get; set; }
	}

#if AG_PUBLIC_API
	public
#endif
	enum InterceptType
	{
		OnInit,
		OnUsing,
		OnBeforeCall,
		OnAfterCall,
		OnCatch,
		OnFinally
	}

#if AG_PUBLIC_API
	public
#endif
	enum InterceptResult
	{
		Continue,
		Return,
		ReThrow     = Continue,
		IgnoreThrow = Return
	}

#if AG_PUBLIC_API
	public
#endif
	struct Void
	{
	}

#if AG_PUBLIC_API
	public
#endif
	partial class InterceptInfo
	{
		public object?         Tag;
		public InterceptType   InterceptType;
		public InterceptResult InterceptResult;
		public Exception?      Exception;

		public InterceptInfo?                                        PreviousInfo;
		public System.Reflection.MemberInfo                          MemberInfo;
		public object?[]?                                            MethodArguments;
		public Type                                                  AspectType;
		public System.Collections.Generic.Dictionary<string,object?> AspectArguments;
	}

#if AG_PUBLIC_API
	public
#endif
	partial class InterceptInfo<T> : InterceptInfo
	{
		public T ReturnValue;
	}

#if AG_PUBLIC_API
	public
#endif
	partial struct InterceptData<T>
	{
		public object?         Tag;
		public InterceptType   InterceptType;
		public InterceptResult InterceptResult;
		public Exception?      Exception;

		public InterceptInfo<T>?                                     PreviousInfo;
		public System.Reflection.MemberInfo                          MemberInfo;
		public object?[]?                                            MethodArguments;
		public Type                                                  AspectType;
		public System.Collections.Generic.Dictionary<string,object?> AspectArguments;

		public T ReturnValue;
	}
}

#endif

#if AG_GENERATE_InterceptsLocationAttribute || !AG_NOT_GENERATE_InterceptsLocationAttribute

namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
	sealed class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
	{
	}
}

#endif

// <auto-generated/>
#pragma warning disable
#nullable enable

using System;

using SR  = System.Reflection;
using SLE = System.Linq.Expressions;
using SCG = System.Collections.Generic;

namespace AspectGenerator
{
	using AspectGenerator = AspectGenerator;

	static partial class Interceptors
	{
		static SR.MethodInfo GetMethodInfo(SLE.Expression expr)
		{
			return expr switch
			{
				SLE.MethodCallExpression mc => mc.Method,
				_                           => throw new InvalidOperationException()
			};
		}

		static SR.MethodInfo MethodOf<T>(SLE.Expression<Func<T>> func) => GetMethodInfo(func.Body);
		static SR.MethodInfo MethodOf   (SLE.Expression<Action>  func) => GetMethodInfo(func.Body);

		static SR. MemberInfo                 FullName_Interceptor_MemberInfo        = MethodOf(() => default(AG.Person).FullName());
		static SCG.Dictionary<string,object?> FullName_Interceptor_AspectArguments_0 = new()
		{
		};
		//
		/// <summary>
		/// Intercepts AG.Person.FullName().
		/// </summary>
		//
		// Intercepts p.FullName().
		[System.Runtime.CompilerServices.InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\AspectGenerator\src\AG\Program.cs", line: 4, character: 10)]
		//
		[System.Runtime.CompilerServices.CompilerGenerated]
		//[System.Diagnostics.DebuggerStepThrough]
		public static string FullName_Interceptor(this AG.Person __this__)
		{
			// AG.MetricsAttribute
			//
			var __info__0 = new AspectGenerator.InterceptInfo<string>
			{
				MemberInfo      = FullName_Interceptor_MemberInfo,
				AspectType      = typeof(AG.MetricsAttribute),
				AspectArguments = FullName_Interceptor_AspectArguments_0,
			};

			using (AG.MetricsAttribute.OnUsing(__info__0))
			{
				__info__0.ReturnValue = __this__.FullName();
			}

			return __info__0.ReturnValue;
		}
	}
}

Code and pdf at

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

RSCG – mocklis

RSCG – mocklis
 
 

name mocklis
nuget https://www.nuget.org/packages/mocklis/
link https://mocklis.readthedocs.io/en/latest/getting-started/index.html
author Esbjörn Redmo

Generating mocks from classes for unit tests

 

This is how you can use mocklis .

The code that you start with is


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

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

    <IsPackable>false</IsPackable>
    <IsTestProject>true</IsTestProject>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0-preview-23577-04" />
    <PackageReference Include="Mocklis" Version="1.4.0-alpha.2" />
    <PackageReference Include="MSTest.TestAdapter" Version="3.2.0-preview.23623.1" />
    <PackageReference Include="MSTest.TestFramework" Version="3.2.0-preview.23623.1" />
    <PackageReference Include="coverlet.collector" Version="6.0.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\MockLisClock\MockLisClock.csproj" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

</Project>


The code that you will use is


namespace TestClock;

[MocklisClass]
public partial class TestMock : IMyClock
{

}



using Mocklis;

namespace TestClock;

[TestClass]
public class TestClock
{
    [TestMethod]
    public void TestMyClock()
    {
        var mockSetup = new TestMock();
        mockSetup.GetNow.Return(DateTime.Now.AddYears(-1));

        // When testing the mock like this you need to cast to the interface.
        // This is different from e.g. Moq where the mocked instance and the 'programming interface' are different things.
        // With Mocklis they are the same. The 99% case is where the mock is passed to another constructor as a dependency,
        // in which case there's an implicit cast to the interface.
        var mock = (IMyClock)mockSetup;
        var data = mock.GetNow();
        Assert.AreEqual(DateTime.Now.Year - 1, data.Year);

    }
}


global using Microsoft.VisualStudio.TestTools.UnitTesting;
global using MockTest;
global using Mocklis.Core;

 

The code that is generated is

// <auto-generated />

#nullable enable

namespace TestClock
{
    partial class TestMock
    {
        public global::Mocklis.Core.FuncMethodMock<global::System.DateTime> GetNow { get; }

        global::System.DateTime global::MockTest.IMyClock.GetNow() => GetNow.Call();

        public global::Mocklis.Core.FuncMethodMock<global::System.DateTime> GetUtcNow { get; }

        global::System.DateTime global::MockTest.IMyClock.GetUtcNow() => GetUtcNow.Call();

        public TestMock() : base()
        {
            this.GetNow = new global::Mocklis.Core.FuncMethodMock<global::System.DateTime>(this, "TestMock", "IMyClock", "GetNow", "GetNow", global::Mocklis.Core.Strictness.Lenient);
            this.GetUtcNow = new global::Mocklis.Core.FuncMethodMock<global::System.DateTime>(this, "TestMock", "IMyClock", "GetUtcNow", "GetUtcNow", global::Mocklis.Core.Strictness.Lenient);
        }
    }
}

Code and pdf at

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

RSCG – RSCG_UtilityTypes

RSCG – RSCG_UtilityTypes
 
 

name RSCG_UtilityTypes
nuget https://www.nuget.org/packages/RSCG_UtilityTypes/
https://www.nuget.org/packages/RSCG_UtilityTypesCommon
link https://github.com/ignatandrei/RSCG_UtilityTypes
author Andrei Ignat

Add omit and pick to selectively generate types from existing types

 

This is how you can use RSCG_UtilityTypes .

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="RSCG_UtilityTypes" Version="2023.1223.1230" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
	  <PackageReference Include="RSCG_UtilityTypesCommon" Version="2023.1223.1230" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using UtilDemo;

var p=new PersonFull();
p.FirstName="Andrei";
p.LastName="Ignat";
Person1 p1=(Person1)p ;
Person2 p2=(Person2)p ;
Console.WriteLine(p1.FirstName);
Console.WriteLine(p2.LastName);


using RSCG_UtilityTypesCommon;

namespace UtilDemo;
[Pick("Person1",nameof(FirstName),nameof(LastName))]
[Omit("Person2", nameof(Salary))]
public class PersonFull
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Salary { get; set; }

}


 

The code that is generated is

namespace UtilDemo
{
partial class Person1
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

public static explicit operator Person1(PersonFull data )
    {
        var ret= new Person1 ();
        ret.FirstName = data.FirstName;
ret.LastName = data.LastName;
        return ret;
    }



public static explicit operator PersonFull(Person1 data )
    {
        var ret= new PersonFull ();
        ret.FirstName = data.FirstName;
ret.LastName = data.LastName;
        return ret;
    }


}
}

namespace UtilDemo
{
partial class Person2
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

public static explicit operator Person2(PersonFull data )
    {
        var ret= new Person2 ();
        ret.FirstName = data.FirstName;
ret.LastName = data.LastName;
        return ret;
    }



public static explicit operator PersonFull(Person2 data )
    {
        var ret= new PersonFull ();
        ret.FirstName = data.FirstName;
ret.LastName = data.LastName;
        return ret;
    }


}
}

Code and pdf at

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

RSCG – Ling.Audit

RSCG – Ling.Audit
 
 

name Ling.Audit
nuget https://www.nuget.org/packages/Ling.Audit/
link https://github.com/ling921/dotnet-lib/
author Jing Ling

Generating audit data from class implementation of interfaces

 

This is how you can use Ling.Audit .

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


The code that you will use is


// See https://aka.ms/new-console-template for more information
using LingDemo;

Console.WriteLine("Hello, World!");
var p = new Person();
await Task.Delay(2000);
p.FirstName = "Andrei";
p.LastName = "Ignat";
Console.WriteLine(p.CreationTime);
Console.WriteLine(p.LastModificationTime);


using Ling.Audit;

namespace LingDemo;
partial class Person :IFullAudited<Guid>
{
    public int ID { get; set; }
    public string FirstName { get; set; }= string.Empty;
    public string LastName { get; set; } = string.Empty;
}


 

The code that is generated is

// <auto-generated/>

#nullable enable annotations
#nullable disable warnings

namespace LingDemo
{
    partial class Person
    {
        /// <summary>
        /// Gets or sets the creation time of this entity.
        /// </summary>
        public virtual global::System.DateTimeOffset CreationTime { get; set; }
    
        /// <summary>
        /// Gets or sets the creator Id of this entity.
        /// </summary>
        public virtual global::System.Nullable<global::System.Guid> CreatorId { get; set; }
    
        /// <summary>
        /// Gets or sets the last modification time of this entity.
        /// </summary>
        public virtual global::System.Nullable<global::System.DateTimeOffset> LastModificationTime { get; set; }
    
        /// <summary>
        /// Gets or sets the last modifier Id of this entity.
        /// </summary>
        public virtual global::System.Nullable<global::System.Guid> LastModifierId { get; set; }
    
        /// <summary>
        /// Gets or sets whether this entity is soft deleted.
        /// </summary>
        public virtual global::System.Boolean IsDeleted { get; set; }
    
        /// <summary>
        /// Gets or sets the deletion time of this entity.
        /// </summary>
        public virtual global::System.Nullable<global::System.DateTimeOffset> DeletionTime { get; set; }
    
        /// <summary>
        /// Get or set the deleter Id of this entity.
        /// </summary>
        public virtual global::System.Nullable<global::System.Guid> DeleterId { get; set; }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Ling.Audit

RSCG – TelemetryLogging

RSCG – TelemetryLogging
 
 

name TelemetryLogging
nuget https://www.nuget.org/packages/Microsoft.Extensions.Telemetry.Abstractions/
link https://andrewlock.net/behind-logproperties-and-the-new-telemetry-logging-source-generator/
author Microsoft

Generating deep logging messages for a class

 

This is how you can use TelemetryLogging .

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.Extensions.Logging.Console" Version="8.0.0" />
		<PackageReference Include="Microsoft.Extensions.Telemetry.Abstractions" Version="8.0.0" />
	</ItemGroup>
</Project>


The code that you will use is


using System.Text.Json;
using Microsoft.Extensions.Logging;

using ILoggerFactory loggerFactory = LoggerFactory.Create(
    builder =>
    {
        //builder.AddSimpleConsole();
        builder.AddJsonConsole(
            options =>
            options.JsonWriterOptions = new JsonWriterOptions()
            {
                Indented = true
            });
    }
        
    ) ;

ILogger<Person> logger = loggerFactory.CreateLogger<Person>();
logger.LogInformation("test");
(new LoggingSample(logger)).TestLogging();
public record Person (string firstName, string LastName)
{
}



using Microsoft.Extensions.Logging;

public partial class LoggingSample
{
    private readonly ILogger _logger;

    public LoggingSample(ILogger logger)
    {
        _logger = logger;
    }

    [LoggerMessage(
        EventId = 20,
        Level = LogLevel.Critical,
        Message = "Value is {value:E}")]
    public static partial void UsingFormatSpecifier(
        ILogger logger, double value);

    [LoggerMessage(
        EventId = 19,
        Level = LogLevel.Information,
        Message = "Logging all person properties",
        EventName = "PersonLogging")]
    public partial void LogWithProperties([LogProperties] Person person);


    [LoggerMessage(
        EventId = 9,
        Level = LogLevel.Trace,
        Message = "Fixed message",
        EventName = "CustomEventName")]
    public partial void LogWithCustomEventName();

    [LoggerMessage(
        EventId = 10,
        Message = "Welcome to {city} {province}!")]
    public partial void LogWithDynamicLogLevel(
        string city, LogLevel level, string province);

    public void  TestLogging()
    {
        LogWithProperties(new Person("Andrei", "Ignat"));
        //LogWithCustomEventName();

        //LogWithDynamicLogLevel("Vancouver", LogLevel.Warning, "BC");
        //LogWithDynamicLogLevel("Vancouver", LogLevel.Information, "BC");

        //UsingFormatSpecifier(_logger, 12345.6789);
    }
}

 

The code that is generated is


// <auto-generated/>
#nullable enable
#pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103
partial class LoggingSample
{
    /// <summary>
    /// Logs "Value is {value:E}" at "Critical" level.
    /// </summary>
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Gen.Logging", "8.0.0.0")]
    public static partial void UsingFormatSpecifier(global::Microsoft.Extensions.Logging.ILogger logger, double value)
    {
        var state = global::Microsoft.Extensions.Logging.LoggerMessageHelper.ThreadLocalState;

        _ = state.ReserveTagSpace(2);
        state.TagArray[1] = new("value", value);
        state.TagArray[0] = new("{OriginalFormat}", "Value is {value:E}");

        logger.Log(
            global::Microsoft.Extensions.Logging.LogLevel.Critical,
            new(20, nameof(UsingFormatSpecifier)),
            state,
            null,
            static (s, _) =>
            {
                var value = s.TagArray[1].Value;
                return global::System.FormattableString.Invariant($"Value is {value:E}");
            });

        state.Clear();
    }

    /// <summary>
    /// Logs "Logging all person properties" at "Information" level.
    /// </summary>
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Gen.Logging", "8.0.0.0")]
    public partial void LogWithProperties(global::Person person)
    {
        if (!_logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Information))
        {
            return;
        }

        var state = global::Microsoft.Extensions.Logging.LoggerMessageHelper.ThreadLocalState;

        _ = state.ReserveTagSpace(3);
        state.TagArray[2] = new("person.firstName", person?.firstName);
        state.TagArray[1] = new("person.LastName", person?.LastName);
        state.TagArray[0] = new("{OriginalFormat}", "Logging all person properties");

        _logger.Log(
            global::Microsoft.Extensions.Logging.LogLevel.Information,
            new(19, "PersonLogging"),
            state,
            null,
            static (s, _) =>
            {
                return "Logging all person properties";
            });

        state.Clear();
    }

    /// <summary>
    /// Logs "Fixed message" at "Trace" level.
    /// </summary>
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Gen.Logging", "8.0.0.0")]
    public partial void LogWithCustomEventName()
    {
        if (!_logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Trace))
        {
            return;
        }

        var state = global::Microsoft.Extensions.Logging.LoggerMessageHelper.ThreadLocalState;

        _ = state.ReserveTagSpace(1);
        state.TagArray[0] = new("{OriginalFormat}", "Fixed message");

        _logger.Log(
            global::Microsoft.Extensions.Logging.LogLevel.Trace,
            new(9, "CustomEventName"),
            state,
            null,
            static (s, _) =>
            {
                return "Fixed message";
            });

        state.Clear();
    }

    /// <summary>
    /// Logs "Welcome to {city} {province}!".
    /// </summary>
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Gen.Logging", "8.0.0.0")]
    public partial void LogWithDynamicLogLevel(string city, global::Microsoft.Extensions.Logging.LogLevel level, string province)
    {
        if (!_logger.IsEnabled(level))
        {
            return;
        }

        var state = global::Microsoft.Extensions.Logging.LoggerMessageHelper.ThreadLocalState;

        _ = state.ReserveTagSpace(3);
        state.TagArray[2] = new("city", city);
        state.TagArray[1] = new("province", province);
        state.TagArray[0] = new("{OriginalFormat}", "Welcome to {city} {province}!");

        _logger.Log(
            level,
            new(10, nameof(LogWithDynamicLogLevel)),
            state,
            null,
            static (s, _) =>
            {
                var city = s.TagArray[2].Value ?? "(null)";
                var province = s.TagArray[1].Value ?? "(null)";
                return global::System.FormattableString.Invariant($"Welcome to {city} {province}!");
            });

        state.Clear();
    }
}

Code and pdf at

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

RSCG – Com

RSCG – Com    

name Com
nuget https://www.nuget.org/packages/System.Runtime.InteropServices/
link https://learn.microsoft.com/en-us/dotnet/standard/native-interop/comwrappers-source-generation
author Microsoft

Generating Com Declarations

 

This is how you can use Com .

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> 	  <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <!--
<PackageReference Include="System.Runtime.InteropServices" />
-->   </PropertyGroup> 	<PropertyGroup> 		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> 		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath> 	</PropertyGroup> </Project>   

The code that you will use is

  using test;  IShellExecute shellExecute = new ShellExecuteClass();  // Open a file using the default associated program IntPtr result = shellExecute.ShellExecute(     IntPtr.Zero, // HWND (handle to parent window)     "open",      // Operation to perform     "notepad.exe", // File to open (replace with your file or URL)     "",           // Parameters     "",           // Working directory     1            // Show command (SW_SHOWNORMAL) );  Console.WriteLine($"ShellExecute Result: {result}");   
  using System; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling;  namespace test;  // Import the ShellExecute function from Shell32.dll using ComImport [ComImport] [Guid("00000000-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IUnknown {     void QueryInterface(ref Guid riid, out IntPtr ppvObject);     void AddRef();     void Release(); }  //[ComImport] [GeneratedComInterface(StringMarshalling = StringMarshalling.Utf8)] [Guid("000214F9-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public partial interface IShellExecute {     IntPtr ShellExecute(IntPtr hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd); }   // Replace this with your actual ShellExecute COM class public class ShellExecuteClass : IShellExecute {     public IntPtr ShellExecute(IntPtr hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd)     {         // Implement the ShellExecute functionality         return NativeMethods.ShellExecute(hwnd, lpOperation, lpFile, lpParameters, lpDirectory, nShowCmd);     } }  // NativeMethods class to import necessary functions from Shell32.dll static class NativeMethods {     [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]     public static extern IntPtr ShellExecute(         IntPtr hwnd,         string lpOperation,         string lpFile,         string lpParameters,         string lpDirectory,         int nShowCmd     ); }   

  The code that is generated is

 // <auto-generated /> #pragma warning disable CS0612, CS0618 file unsafe class InterfaceInformation : global::System.Runtime.InteropServices.Marshalling.IIUnknownInterfaceType {     public static global::System.Guid Iid { get; } = new(new global::System.ReadOnlySpan<byte>(new byte[] { 249, 20, 2, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 70 }));      private static void** _vtable;     public static void** ManagedVirtualMethodTable => _vtable != null ? _vtable : (_vtable = InterfaceImplementation.CreateManagedVirtualFunctionTable()); }  [global::System.Runtime.InteropServices.DynamicInterfaceCastableImplementationAttribute] file unsafe partial interface InterfaceImplementation : global::test.IShellExecute {     [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.ComInterfaceGenerator", "8.0.9.3103")]     [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute]     nint global::test.IShellExecute.ShellExecute(nint hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd)     {         var(__this, __vtable_native) = ((global::System.Runtime.InteropServices.Marshalling.IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(typeof(global::test.IShellExecute));         byte* __lpOperation_native = default;         byte* __lpFile_native = default;         byte* __lpParameters_native = default;         byte* __lpDirectory_native = default;         nint __retVal = default;         int __invokeRetVal = default;         // Setup - Perform required setup.         scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __lpDirectory_native__marshaller = new();         scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __lpParameters_native__marshaller = new();         scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __lpFile_native__marshaller = new();         scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __lpOperation_native__marshaller = new();         try         {             // Marshal - Convert managed data to native data.             __lpDirectory_native__marshaller.FromManaged(lpDirectory, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]);             __lpParameters_native__marshaller.FromManaged(lpParameters, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]);             __lpFile_native__marshaller.FromManaged(lpFile, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]);             __lpOperation_native__marshaller.FromManaged(lpOperation, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]);             {                 // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned.                 __lpDirectory_native = __lpDirectory_native__marshaller.ToUnmanaged();                 __lpParameters_native = __lpParameters_native__marshaller.ToUnmanaged();                 __lpFile_native = __lpFile_native__marshaller.ToUnmanaged();                 __lpOperation_native = __lpOperation_native__marshaller.ToUnmanaged();                 __invokeRetVal = ((delegate* unmanaged[MemberFunction]<void*, nint, byte*, byte*, byte*, byte*, int, nint*, int> )__vtable_native[3])(__this, hwnd, __lpOperation_native, __lpFile_native, __lpParameters_native, __lpDirectory_native, nShowCmd, &__retVal);             }              // NotifyForSuccessfulInvoke - Keep alive any managed objects that need to stay alive across the call.             global::System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(__invokeRetVal);             global::System.GC.KeepAlive(this);         }         finally         {             // CleanupCallerAllocated - Perform cleanup of caller allocated resources.             __lpDirectory_native__marshaller.Free();             __lpParameters_native__marshaller.Free();             __lpFile_native__marshaller.Free();             __lpOperation_native__marshaller.Free();         }          return __retVal;     } }  file unsafe partial interface InterfaceImplementation {     [global::System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute(CallConvs = new[] { typeof(global::System.Runtime.CompilerServices.CallConvMemberFunction) })]     internal static int ABI_ShellExecute(global::System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch* __this_native, nint hwnd, byte* __lpOperation_native, byte* __lpFile_native, byte* __lpParameters_native, byte* __lpDirectory_native, int nShowCmd, nint* __invokeRetValUnmanaged__param)     {         global::test.IShellExecute @this = default;         string lpOperation = default;         string lpFile = default;         string lpParameters = default;         string lpDirectory = default;         ref nint __invokeRetValUnmanaged = ref *__invokeRetValUnmanaged__param;         nint __invokeRetVal = default;         int __retVal = default;         try         {             // Unmarshal - Convert native data to managed data.             lpDirectory = global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ConvertToManaged(__lpDirectory_native);             lpParameters = global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ConvertToManaged(__lpParameters_native);             lpFile = global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ConvertToManaged(__lpFile_native);             lpOperation = global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ConvertToManaged(__lpOperation_native);             @this = global::System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch.GetInstance<global::test.IShellExecute>(__this_native);             __invokeRetVal = @this.ShellExecute(hwnd, lpOperation, lpFile, lpParameters, lpDirectory, nShowCmd);             // NotifyForSuccessfulInvoke - Keep alive any managed objects that need to stay alive across the call.             __retVal = 0; // S_OK             // Marshal - Convert managed data to native data.             __invokeRetValUnmanaged = __invokeRetVal;         }         catch (global::System.Exception __exception)         {             __retVal = global::System.Runtime.InteropServices.Marshalling.ExceptionAsHResultMarshaller<int>.ConvertToUnmanaged(__exception);         }          return __retVal;     } }  file unsafe partial interface InterfaceImplementation {     internal static void** CreateManagedVirtualFunctionTable()     {         void** vtable = (void**)global::System.Runtime.CompilerServices.RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(global::test.IShellExecute), sizeof(void*) * 4);         {             nint v0, v1, v2;             global::System.Runtime.InteropServices.ComWrappers.GetIUnknownImpl(out v0, out v1, out v2);             vtable[0] = (void*)v0;             vtable[1] = (void*)v1;             vtable[2] = (void*)v2;         }          {             vtable[3] = (void*)(delegate* unmanaged[MemberFunction]<global::System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch*, nint, byte*, byte*, byte*, byte*, int, nint*, int> )&ABI_ShellExecute;         }          return vtable;     } }  namespace test {     [global::System.Runtime.InteropServices.Marshalling.IUnknownDerivedAttribute<InterfaceInformation, InterfaceImplementation>]     public partial interface IShellExecute     {     } }  namespace test {     public partial interface IShellExecute     {     } } 

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

RSCG – RDG

RSCG – RDG    

name RDG
nuget https://www.nuget.org/packages/Microsoft.Extensions.Http
link https://learn.microsoft.com/en-us/aspnet/core/fundamentals/aot/request-delegate-generator/rdg?view=aspnetcore-8.0
author Microsoft

Generating replacing for minimal API Map

 

This is how you can use RDG .

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.Http"></PackageReference>
		--> 		<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" /> 		<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> 	</ItemGroup>  	<PropertyGroup> 		<EnableRequestDelegateGenerator>true</EnableRequestDelegateGenerator> 	</PropertyGroup>   	<PropertyGroup> 		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> 		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath> 	</PropertyGroup> </Project>   

The code that you will use is

  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(); }  var summaries = new[] {     "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };  app.MapGet("/weatherforecast", () => {     var forecast = Enumerable.Range(1, 5).Select(index =>         new WeatherForecast         (             DateOnly.FromDateTime(DateTime.Now.AddDays(index)),             Random.Shared.Next(-20, 55),             summaries[Random.Shared.Next(summaries.Length)]         ))         .ToArray();     return forecast; }) .WithName("GetWeatherForecast") .WithOpenApi();  app.Run();  internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) {     public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); }   

  The code that is generated is

 //------------------------------------------------------------------------------ // <auto-generated> //     This code was generated by a tool. // //     Changes to this file may cause incorrect behavior and will be lost if //     the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ #nullable enable  namespace System.Runtime.CompilerServices {     [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "8.0.0.0")]     [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]     file sealed class InterceptsLocationAttribute : Attribute     {         public InterceptsLocationAttribute(string filePath, int line, int column)         {         }     } }  namespace Microsoft.AspNetCore.Http.Generated {     using System;     using System.Collections;     using System.Collections.Generic;     using System.Collections.ObjectModel;     using System.Diagnostics;     using System.Diagnostics.CodeAnalysis;     using System.Globalization;     using System.Linq;     using System.Reflection;     using System.Runtime.CompilerServices;     using System.Text.Json;     using System.Text.Json.Serialization.Metadata;     using System.Threading.Tasks;     using System.IO;     using Microsoft.AspNetCore.Antiforgery;     using Microsoft.AspNetCore.Routing;     using Microsoft.AspNetCore.Routing.Patterns;     using Microsoft.AspNetCore.Builder;     using Microsoft.AspNetCore.Http;     using Microsoft.AspNetCore.Http.Json;     using Microsoft.AspNetCore.Http.Metadata;     using Microsoft.Extensions.DependencyInjection;     using Microsoft.Extensions.FileProviders;     using Microsoft.Extensions.Logging;     using Microsoft.Extensions.Primitives;     using Microsoft.Extensions.Options;      using MetadataPopulator = System.Func<System.Reflection.MethodInfo, Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions?, Microsoft.AspNetCore.Http.RequestDelegateMetadataResult>;     using RequestDelegateFactoryFunc = System.Func<System.Delegate, Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions, Microsoft.AspNetCore.Http.RequestDelegateMetadataResult?, Microsoft.AspNetCore.Http.RequestDelegateResult>;      [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "8.0.0.0")]     file static class GeneratedRouteBuilderExtensionsCore     {         private static readonly JsonOptions FallbackJsonOptions = new();         private static readonly string[] GetVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Get };          [InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\RDG\src\RDGDemo\RDGDemoWebApi\Program.cs", 22, 5)]         internal static RouteHandlerBuilder MapGet0(             this IEndpointRouteBuilder endpoints,             [StringSyntax("Route")] string pattern,             Delegate handler)         {             MetadataPopulator populateMetadata = (methodInfo, options) =>             {                 Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");                 Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");                 options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "8.0.0.0"));                 options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::WeatherForecast[]), contentTypes: GeneratedMetadataConstants.JsonContentType));                 return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() };             };             RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) =>             {                 Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");                 Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");                 Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found.");                 Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found.");                 var handler = Cast(del, global::WeatherForecast[] () => throw null!);                 EndpointFilterDelegate? filteredInvocation = null;                 var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;                 var jsonOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value ?? FallbackJsonOptions;                 var jsonSerializerOptions = jsonOptions.SerializerOptions;                 jsonSerializerOptions.MakeReadOnly();                 var objectJsonTypeInfo = (JsonTypeInfo<object?>)jsonSerializerOptions.GetTypeInfo(typeof(object));                 var responseJsonTypeInfo =  (JsonTypeInfo<global::WeatherForecast&#91;&#93;?>)jsonSerializerOptions.GetTypeInfo(typeof(global::WeatherForecast[]));                  if (options.EndpointBuilder.FilterFactories.Count > 0)                 {                     filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic =>                     {                         if (ic.HttpContext.Response.StatusCode == 400)                         {                             return ValueTask.FromResult<object?>(Results.Empty);                         }                         return ValueTask.FromResult<object?>(handler());                     },                     options.EndpointBuilder,                     handler.Method);                 }                  Task RequestHandler(HttpContext httpContext)                 {                     var wasParamCheckFailure = false;                     if (wasParamCheckFailure)                     {                         httpContext.Response.StatusCode = 400;                         return Task.CompletedTask;                     }                     var result = handler();                     return GeneratedRouteBuilderExtensionsCore.WriteJsonResponseAsync(httpContext.Response, result, responseJsonTypeInfo);                 }                  async Task RequestHandlerFiltered(HttpContext httpContext)                 {                     var wasParamCheckFailure = false;                     if (wasParamCheckFailure)                     {                         httpContext.Response.StatusCode = 400;                     }                     var result = await filteredInvocation(EndpointFilterInvocationContext.Create(httpContext));                     if (result is not null)                     {                         await GeneratedRouteBuilderExtensionsCore.ExecuteReturnAsync(result, httpContext, objectJsonTypeInfo);                     }                 }                  RequestDelegate targetDelegate = filteredInvocation is null ? RequestHandler : RequestHandlerFiltered;                 var metadata = inferredMetadataResult?.EndpointMetadata ?? ReadOnlyCollection<object>.Empty;                 return new RequestDelegateResult(targetDelegate, metadata);             };             return MapCore(                 endpoints,                 pattern,                 handler,                 GetVerb,                 populateMetadata,                 createRequestDelegate);         }            internal static RouteHandlerBuilder MapCore(             this IEndpointRouteBuilder routes,             string pattern,             Delegate handler,             IEnumerable<string>? httpMethods,             MetadataPopulator populateMetadata,             RequestDelegateFactoryFunc createRequestDelegate)         {             return RouteHandlerServices.Map(routes, pattern, handler, httpMethods, populateMetadata, createRequestDelegate);         }          private static T Cast<T>(Delegate d, T _) where T : Delegate         {             return (T)d;         }          private static EndpointFilterDelegate BuildFilterDelegate(EndpointFilterDelegate filteredInvocation, EndpointBuilder builder, MethodInfo mi)         {             var routeHandlerFilters =  builder.FilterFactories;             var context0 = new EndpointFilterFactoryContext             {                 MethodInfo = mi,                 ApplicationServices = builder.ApplicationServices,             };             var initialFilteredInvocation = filteredInvocation;             for (var i = routeHandlerFilters.Count - 1; i >= 0; i--)             {                 var filterFactory = routeHandlerFilters[i];                 filteredInvocation = filterFactory(context0, filteredInvocation);             }             return filteredInvocation;         }          private static Task ExecuteReturnAsync(object? obj, HttpContext httpContext, JsonTypeInfo<object?> jsonTypeInfo)         {             if (obj is IResult r)             {                 return r.ExecuteAsync(httpContext);             }             else if (obj is string s)             {                 return httpContext.Response.WriteAsync(s);             }             else             {                 return WriteJsonResponseAsync(httpContext.Response, obj, jsonTypeInfo);             }         }          [UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",             Justification = "The 'JsonSerializer.IsReflectionEnabledByDefault' feature switch, which is set to false by default for trimmed ASP.NET apps, ensures the JsonSerializer doesn't use Reflection.")]         [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", Justification = "See above.")]         private static Task WriteJsonResponseAsync<T>(HttpResponse response, T? value, JsonTypeInfo<T?> jsonTypeInfo)         {             var runtimeType = value?.GetType();              if (jsonTypeInfo.ShouldUseWith(runtimeType))             {                 return HttpResponseJsonExtensions.WriteAsJsonAsync(response, value, jsonTypeInfo, default);             }              return response.WriteAsJsonAsync<object?>(value, jsonTypeInfo.Options);         }          private static bool HasKnownPolymorphism(this JsonTypeInfo jsonTypeInfo)             => jsonTypeInfo.Type.IsSealed || jsonTypeInfo.Type.IsValueType || jsonTypeInfo.PolymorphismOptions is not null;          private static bool ShouldUseWith(this JsonTypeInfo jsonTypeInfo, [NotNullWhen(false)] Type? runtimeType)             => runtimeType is null || jsonTypeInfo.Type == runtimeType || jsonTypeInfo.HasKnownPolymorphism();       }      [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "8.0.0.0")]     file static class GeneratedMetadataConstants     {         public static readonly string[] JsonContentType = new [] { "application/json" };         public static readonly string[] PlaintextContentType = new [] { "text/plain" };         public static readonly string[] FormFileContentType = new[] { "multipart/form-data" };         public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" };     }       [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "8.0.0.0")]     file sealed class LogOrThrowExceptionHelper     {         private readonly ILogger? _rdgLogger;         private readonly bool _shouldThrow;          public LogOrThrowExceptionHelper(IServiceProvider? serviceProvider, RequestDelegateFactoryOptions? options)         {             var loggerFactory = serviceProvider?.GetRequiredService<ILoggerFactory>();             _rdgLogger = loggerFactory?.CreateLogger("Microsoft.AspNetCore.Http.RequestDelegateGenerator.RequestDelegateGenerator");             _shouldThrow = options?.ThrowOnBadRequest ?? false;         }          public void RequestBodyIOException(IOException exception)         {             if (_rdgLogger != null)             {                 _requestBodyIOException(_rdgLogger, exception);             }         }          private static readonly Action<ILogger, Exception?> _requestBodyIOException =             LoggerMessage.Define(LogLevel.Debug, new EventId(1, "RequestBodyIOException"), "Reading the request body failed with an IOException.");          public void InvalidJsonRequestBody(string parameterTypeName, string parameterName, Exception exception)         {             if (_shouldThrow)             {                 var message = string.Format(CultureInfo.InvariantCulture, "Failed to read parameter \"{0} {1}\" from the request body as JSON.", parameterTypeName, parameterName);                 throw new BadHttpRequestException(message, exception);             }              if (_rdgLogger != null)             {                 _invalidJsonRequestBody(_rdgLogger, parameterTypeName, parameterName, exception);             }         }          private static readonly Action<ILogger, string, string, Exception?> _invalidJsonRequestBody =             LoggerMessage.Define<string, string>(LogLevel.Debug, new EventId(2, "InvalidJsonRequestBody"), "Failed to read parameter \"{ParameterType} {ParameterName}\" from the request body as JSON.");          public void ParameterBindingFailed(string parameterTypeName, string parameterName, string sourceValue)         {             if (_shouldThrow)             {                 var message = string.Format(CultureInfo.InvariantCulture, "Failed to bind parameter \"{0} {1}\" from \"{2}\".", parameterTypeName, parameterName, sourceValue);                 throw new BadHttpRequestException(message);             }              if (_rdgLogger != null)             {                 _parameterBindingFailed(_rdgLogger, parameterTypeName, parameterName, sourceValue, null);             }         }          private static readonly Action<ILogger, string, string, string, Exception?> _parameterBindingFailed =             LoggerMessage.Define<string, string, string>(LogLevel.Debug, new EventId(3, "ParameterBindingFailed"), "Failed to bind parameter \"{ParameterType} {ParameterName}\" from \"{SourceValue}\".");          public void RequiredParameterNotProvided(string parameterTypeName, string parameterName, string source)         {             if (_shouldThrow)             {                 var message = string.Format(CultureInfo.InvariantCulture, "Required parameter \"{0} {1}\" was not provided from {2}.", parameterTypeName, parameterName, source);                 throw new BadHttpRequestException(message);             }              if (_rdgLogger != null)             {                 _requiredParameterNotProvided(_rdgLogger, parameterTypeName, parameterName, source, null);             }         }          private static readonly Action<ILogger, string, string, string, Exception?> _requiredParameterNotProvided =             LoggerMessage.Define<string, string, string>(LogLevel.Debug, new EventId(4, "RequiredParameterNotProvided"), "Required parameter \"{ParameterType} {ParameterName}\" was not provided from {Source}.");          public void ImplicitBodyNotProvided(string parameterName)         {             if (_shouldThrow)             {                 var message = string.Format(CultureInfo.InvariantCulture, "Implicit body inferred for parameter \"{0}\" but no body was provided. Did you mean to use a Service instead?", parameterName);                 throw new BadHttpRequestException(message);             }              if (_rdgLogger != null)             {                 _implicitBodyNotProvided(_rdgLogger, parameterName, null);             }         }          private static readonly Action<ILogger, string, Exception?> _implicitBodyNotProvided =             LoggerMessage.Define<string>(LogLevel.Debug, new EventId(5, "ImplicitBodyNotProvided"), "Implicit body inferred for parameter \"{ParameterName}\" but no body was provided. Did you mean to use a Service instead?");          public void UnexpectedJsonContentType(string? contentType)         {             if (_shouldThrow)             {                 var message = string.Format(CultureInfo.InvariantCulture, "Expected a supported JSON media type but got \"{0}\".", contentType);                 throw new BadHttpRequestException(message, StatusCodes.Status415UnsupportedMediaType);             }              if (_rdgLogger != null)             {                 _unexpectedJsonContentType(_rdgLogger, contentType ?? "(none)", null);             }         }          private static readonly Action<ILogger, string, Exception?> _unexpectedJsonContentType =             LoggerMessage.Define<string>(LogLevel.Debug, new EventId(6, "UnexpectedContentType"), "Expected a supported JSON media type but got \"{ContentType}\".");          public void UnexpectedNonFormContentType(string? contentType)         {             if (_shouldThrow)             {                 var message = string.Format(CultureInfo.InvariantCulture, "Expected a supported form media type but got \"{0}\".", contentType);                 throw new BadHttpRequestException(message, StatusCodes.Status415UnsupportedMediaType);             }              if (_rdgLogger != null)             {                 _unexpectedNonFormContentType(_rdgLogger, contentType ?? "(none)", null);             }         }          private static readonly Action<ILogger, string, Exception?> _unexpectedNonFormContentType =             LoggerMessage.Define<string>(LogLevel.Debug, new EventId(7, "UnexpectedNonFormContentType"), "Expected a supported form media type but got \"{ContentType}\".");          public void InvalidFormRequestBody(string parameterTypeName, string parameterName, Exception exception)         {             if (_shouldThrow)             {                 var message = string.Format(CultureInfo.InvariantCulture, "Failed to read parameter \"{0} {1}\" from the request body as form.", parameterTypeName, parameterName);                 throw new BadHttpRequestException(message, exception);             }              if (_rdgLogger != null)             {                 _invalidFormRequestBody(_rdgLogger, parameterTypeName, parameterName, exception);             }         }          private static readonly Action<ILogger, string, string, Exception?> _invalidFormRequestBody =             LoggerMessage.Define<string, string>(LogLevel.Debug, new EventId(8, "InvalidFormRequestBody"), "Failed to read parameter \"{ParameterType} {ParameterName}\" from the request body as form.");     } } 

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

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

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.