RSCG – polytype

RSCG – polytype
 
 

name polytype
nuget https://www.nuget.org/packages/polytype/
link https://github.com/eiriktsarpalis/PolyType
author Eirik Tsarpalis

Generating shape like reflection from classes. See PolyType.Examples for more details

 

This is how you can use polytype .

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="PolyType" Version="0.16.1" />
    <PackageReference Include="PolyType.Examples" Version="0.16.1" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


using PolyType.Examples.JsonSerializer;
using PolyType.Examples.CborSerializer;
using PolyType.Examples.XmlSerializer;
using ConsoleApp1;
using PolyType.Examples.Cloner;

Person person = new("Pete", 70);
Console.WriteLine(JsonSerializerTS.Serialize(person)); // {"Name":"Pete","Age":70}
Console.WriteLine(XmlSerializer.Serialize(person));    // <value><Name>Pete</Name><Age>70</Age></value>
Console.WriteLine(CborSerializer.EncodeToHex(person)); // A2644E616D656450657465634167651846
person.Childs = [new Person("Andrei", 55)];

person.Childs[0].ID = 1;
var q = Cloner.Clone(person);
person.Childs[0].ID = 10;
Console.WriteLine(q);
Console.WriteLine(person);
Console.WriteLine(q.Childs[0]);
Console.WriteLine(person.Childs[0]);



using PolyType;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1;
[GenerateShape]
public partial record Person(string name, int age)
{
    public Person[] Childs { get; set; } = [];

    public int ID;
}

 

The code that is generated is

// <auto-generated/>

#nullable enable annotations
#nullable disable warnings

namespace ConsoleApp1
{
    public partial record Person : global::PolyType.IShapeable<global::ConsoleApp1.Person>
    {
        static global::PolyType.Abstractions.ITypeShape<global::ConsoleApp1.Person> global::PolyType.IShapeable<global::ConsoleApp1.Person>.GetShape() 
            => global::PolyType.SourceGenerator.GenerateShapeProvider.Default.Person;
    }
}

// <auto-generated/>

#nullable enable annotations
#nullable disable warnings

namespace PolyType.SourceGenerator
{
    internal partial class GenerateShapeProvider
    {
        private const global::System.Reflection.BindingFlags InstanceBindingFlags = 
            global::System.Reflection.BindingFlags.Public | 
            global::System.Reflection.BindingFlags.NonPublic | 
            global::System.Reflection.BindingFlags.Instance;
        
        /// <summary>Gets the default instance of the <see cref="GenerateShapeProvider"/> class.</summary>
        public static GenerateShapeProvider Default { get; } = new();
        
        /// <summary>Initializes a new instance of the <see cref="GenerateShapeProvider"/> class.</summary>
        public GenerateShapeProvider() { }
    }
}

// <auto-generated/>

#nullable enable annotations
#nullable disable warnings

namespace PolyType.SourceGenerator
{
    internal partial class GenerateShapeProvider
    {
        /// <summary>Gets the generated shape for specified type.</summary>
#nullable disable annotations // Use nullable-oblivious property type
        public global::PolyType.Abstractions.ITypeShape<int> Int32 => _Int32 ??= Create_Int32();
#nullable enable annotations // Use nullable-oblivious property type
        private global::PolyType.Abstractions.ITypeShape<int>? _Int32;

        private global::PolyType.Abstractions.ITypeShape<int> Create_Int32()
        {
            return new global::PolyType.SourceGenModel.SourceGenObjectTypeShape<int>
            {
                Provider = this,
                IsRecordType = false,
                IsTupleType = false,
                CreatePropertiesFunc = null,
                CreateConstructorFunc = null,
            };
        }
    }
}

// <auto-generated/>

#nullable enable annotations
#nullable disable warnings

namespace PolyType.SourceGenerator
{
    internal partial class GenerateShapeProvider : global::PolyType.ITypeShapeProvider
    {
        /// <summary>
        /// Gets the generated <see cref="global::PolyType.Abstractions.ITypeShape{T}" /> for the specified type.
        /// </summary>
        /// <typeparam name="T">The type for which a shape is requested.</typeparam>
        /// <returns>
        /// The generated <see cref="global::PolyType.Abstractions.ITypeShape{T}" /> for the specified type.
        /// </returns>
        public global::PolyType.Abstractions.ITypeShape<T>? GetShape<T>()
            => (global::PolyType.Abstractions.ITypeShape<T>?)GetShape(typeof(T));
        
        /// <summary>
        /// Gets the generated <see cref="global::PolyType.Abstractions.ITypeShape" /> for the specified type.
        /// </summary>
        /// <param name="type">The type for which a shape is requested.</param>
        /// <returns>
        /// The generated <see cref="global::PolyType.Abstractions.ITypeShape" /> for the specified type.
        /// </returns>
        public global::PolyType.Abstractions.ITypeShape? GetShape(global::System.Type type)
        {
            if (type == typeof(global::ConsoleApp1.Person[]))
                return Person_Array;
            
            if (type == typeof(string))
                return String;
            
            if (type == typeof(global::ConsoleApp1.Person))
                return Person;
            
            if (type == typeof(int))
                return Int32;
            
            return null;
        }
    }
}

// <auto-generated/>

#nullable enable annotations
#nullable disable warnings

namespace PolyType.SourceGenerator
{
    internal partial class GenerateShapeProvider
    {
        /// <summary>Gets the generated shape for specified type.</summary>
#nullable disable annotations // Use nullable-oblivious property type
        public global::PolyType.Abstractions.ITypeShape<global::ConsoleApp1.Person> Person => _Person ??= Create_Person();
#nullable enable annotations // Use nullable-oblivious property type
        private global::PolyType.Abstractions.ITypeShape<global::ConsoleApp1.Person>? _Person;

        private global::PolyType.Abstractions.ITypeShape<global::ConsoleApp1.Person> Create_Person()
        {
            return new global::PolyType.SourceGenModel.SourceGenObjectTypeShape<global::ConsoleApp1.Person>
            {
                Provider = this,
                IsRecordType = true,
                IsTupleType = false,
                CreatePropertiesFunc = CreateProperties_Person,
                CreateConstructorFunc = CreateConstructor_Person,
            };
        }

        private global::PolyType.Abstractions.IPropertyShape[] CreateProperties_Person() => new global::PolyType.Abstractions.IPropertyShape[]
        {
            new global::PolyType.SourceGenModel.SourceGenPropertyShape<global::ConsoleApp1.Person, string>
            {
                Name = "name",
                DeclaringType = (global::PolyType.Abstractions.IObjectTypeShape<global::ConsoleApp1.Person>)Person,
                PropertyType = String,
                Getter = static (ref global::ConsoleApp1.Person obj) => obj.name,
                Setter = null,
                AttributeProviderFunc = static () => typeof(global::ConsoleApp1.Person).GetProperty("name", InstanceBindingFlags, null, typeof(string), [], null),
                IsField = false,
                IsGetterPublic = true,
                IsSetterPublic = false,
                IsGetterNonNullable = true,
                IsSetterNonNullable = false,
            },

            new global::PolyType.SourceGenModel.SourceGenPropertyShape<global::ConsoleApp1.Person, int>
            {
                Name = "age",
                DeclaringType = (global::PolyType.Abstractions.IObjectTypeShape<global::ConsoleApp1.Person>)Person,
                PropertyType = Int32,
                Getter = static (ref global::ConsoleApp1.Person obj) => obj.age,
                Setter = null,
                AttributeProviderFunc = static () => typeof(global::ConsoleApp1.Person).GetProperty("age", InstanceBindingFlags, null, typeof(int), [], null),
                IsField = false,
                IsGetterPublic = true,
                IsSetterPublic = false,
                IsGetterNonNullable = true,
                IsSetterNonNullable = false,
            },

            new global::PolyType.SourceGenModel.SourceGenPropertyShape<global::ConsoleApp1.Person, global::ConsoleApp1.Person[]>
            {
                Name = "Childs",
                DeclaringType = (global::PolyType.Abstractions.IObjectTypeShape<global::ConsoleApp1.Person>)Person,
                PropertyType = Person_Array,
                Getter = static (ref global::ConsoleApp1.Person obj) => obj.Childs,
                Setter = static (ref global::ConsoleApp1.Person obj, global::ConsoleApp1.Person[] value) => obj.Childs = value,
                AttributeProviderFunc = static () => typeof(global::ConsoleApp1.Person).GetProperty("Childs", InstanceBindingFlags, null, typeof(global::ConsoleApp1.Person[]), [], null),
                IsField = false,
                IsGetterPublic = true,
                IsSetterPublic = true,
                IsGetterNonNullable = true,
                IsSetterNonNullable = true,
            },

            new global::PolyType.SourceGenModel.SourceGenPropertyShape<global::ConsoleApp1.Person, int>
            {
                Name = "ID",
                DeclaringType = (global::PolyType.Abstractions.IObjectTypeShape<global::ConsoleApp1.Person>)Person,
                PropertyType = Int32,
                Getter = static (ref global::ConsoleApp1.Person obj) => obj.ID,
                Setter = static (ref global::ConsoleApp1.Person obj, int value) => obj.ID = value,
                AttributeProviderFunc = static () => typeof(global::ConsoleApp1.Person).GetField("ID", InstanceBindingFlags),
                IsField = true,
                IsGetterPublic = true,
                IsSetterPublic = true,
                IsGetterNonNullable = true,
                IsSetterNonNullable = true,
            },
        };

        private global::PolyType.Abstractions.IConstructorShape CreateConstructor_Person()
        {
            return new global::PolyType.SourceGenModel.SourceGenConstructorShape<global::ConsoleApp1.Person, (string, int, global::ConsoleApp1.Person[], int, byte Flags)>
            {
                DeclaringType = (global::PolyType.Abstractions.IObjectTypeShape<global::ConsoleApp1.Person>)Person,
                ParameterCount = 4,
                GetParametersFunc = CreateConstructorParameters_Person,
                DefaultConstructorFunc = null,
                ArgumentStateConstructorFunc = static () => default((string, int, global::ConsoleApp1.Person[], int, byte Flags)),
                ParameterizedConstructorFunc = static (ref (string, int, global::ConsoleApp1.Person[], int, byte Flags) state) => { var obj = new global::ConsoleApp1.Person(state.Item1, state.Item2);  if ((state.Flags & 1) != 0) obj.Childs = state.Item3; if ((state.Flags & 2) != 0) obj.ID = state.Item4; return obj; },
                AttributeProviderFunc = static () => typeof(global::ConsoleApp1.Person).GetConstructor(InstanceBindingFlags, new[] { typeof(string), typeof(int) }),
                IsPublic = true,
            };
        }

        private global::PolyType.Abstractions.IConstructorParameterShape[] CreateConstructorParameters_Person() => new global::PolyType.Abstractions.IConstructorParameterShape[]
        {
            new global::PolyType.SourceGenModel.SourceGenConstructorParameterShape<(string, int, global::ConsoleApp1.Person[], int, byte Flags), string>
            {
                Position = 0,
                Name = "name",
                ParameterType = String,
                Kind = global::PolyType.Abstractions.ConstructorParameterKind.ConstructorParameter,
                IsRequired = true,
                IsNonNullable = true,
                IsPublic = true,
                HasDefaultValue = false,
                DefaultValue = default!,
                Setter = static (ref (string, int, global::ConsoleApp1.Person[], int, byte Flags) state, string value) => state.Item1 = value,
                AttributeProviderFunc = static () => typeof(global::ConsoleApp1.Person).GetConstructor(InstanceBindingFlags, new[] { typeof(string), typeof(int) })?.GetParameters()[0],
            },

            new global::PolyType.SourceGenModel.SourceGenConstructorParameterShape<(string, int, global::ConsoleApp1.Person[], int, byte Flags), int>
            {
                Position = 1,
                Name = "age",
                ParameterType = Int32,
                Kind = global::PolyType.Abstractions.ConstructorParameterKind.ConstructorParameter,
                IsRequired = true,
                IsNonNullable = true,
                IsPublic = true,
                HasDefaultValue = false,
                DefaultValue = default,
                Setter = static (ref (string, int, global::ConsoleApp1.Person[], int, byte Flags) state, int value) => state.Item2 = value,
                AttributeProviderFunc = static () => typeof(global::ConsoleApp1.Person).GetConstructor(InstanceBindingFlags, new[] { typeof(string), typeof(int) })?.GetParameters()[1],
            },

            new global::PolyType.SourceGenModel.SourceGenConstructorParameterShape<(string, int, global::ConsoleApp1.Person[], int, byte Flags), global::ConsoleApp1.Person[]>
            {
                Position = 2,
                Name = "Childs",
                ParameterType = Person_Array,
                Kind = global::PolyType.Abstractions.ConstructorParameterKind.PropertyInitializer,
                IsRequired = false,
                IsNonNullable = true,
                IsPublic = true,
                HasDefaultValue = false,
                DefaultValue = default!,
                Setter = static (ref (string, int, global::ConsoleApp1.Person[], int, byte Flags) state, global::ConsoleApp1.Person[] value) => { state.Item3 = value; state.Flags |= 1; },
                AttributeProviderFunc = static () => typeof(global::ConsoleApp1.Person).GetProperty("Childs", InstanceBindingFlags, null, typeof(global::ConsoleApp1.Person[]), [], null),
            },

            new global::PolyType.SourceGenModel.SourceGenConstructorParameterShape<(string, int, global::ConsoleApp1.Person[], int, byte Flags), int>
            {
                Position = 3,
                Name = "ID",
                ParameterType = Int32,
                Kind = global::PolyType.Abstractions.ConstructorParameterKind.FieldInitializer,
                IsRequired = false,
                IsNonNullable = true,
                IsPublic = true,
                HasDefaultValue = false,
                DefaultValue = default,
                Setter = static (ref (string, int, global::ConsoleApp1.Person[], int, byte Flags) state, int value) => { state.Item4 = value; state.Flags |= 2; },
                AttributeProviderFunc = static () => typeof(global::ConsoleApp1.Person).GetField("ID", InstanceBindingFlags),
            },
        };

        [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Method, Name = "set_name")]
        private static extern void Person_name_SetAccessor(global::ConsoleApp1.Person obj, string value);

        [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Method, Name = "set_age")]
        private static extern void Person_age_SetAccessor(global::ConsoleApp1.Person obj, int value);

        [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Field, Name = "ID")]
        private static extern ref int Person_ID_Accessor(global::ConsoleApp1.Person obj);
    }
}

// <auto-generated/>

#nullable enable annotations
#nullable disable warnings

namespace PolyType.SourceGenerator
{
    internal partial class GenerateShapeProvider
    {
        /// <summary>Gets the generated shape for specified type.</summary>
#nullable disable annotations // Use nullable-oblivious property type
        public global::PolyType.Abstractions.ITypeShape<global::ConsoleApp1.Person[]> Person_Array => _Person_Array ??= Create_Person_Array();
#nullable enable annotations // Use nullable-oblivious property type
        private global::PolyType.Abstractions.ITypeShape<global::ConsoleApp1.Person[]>? _Person_Array;

        private global::PolyType.Abstractions.ITypeShape<global::ConsoleApp1.Person[]> Create_Person_Array()
        {
            return new global::PolyType.SourceGenModel.SourceGenEnumerableTypeShape<global::ConsoleApp1.Person[], global::ConsoleApp1.Person>
            {
                ElementType = Person,
                ConstructionStrategy = global::PolyType.Abstractions.CollectionConstructionStrategy.Span,
                DefaultConstructorFunc = null,
                EnumerableConstructorFunc = null,
                SpanConstructorFunc = static values => values.ToArray(),
                GetEnumerableFunc = static obj => obj,
                AddElementFunc = null,
                Rank = 1,
                Provider = this,
           };
        }
    }
}

// <auto-generated/>

#nullable enable annotations
#nullable disable warnings

namespace PolyType.SourceGenerator
{
    internal partial class GenerateShapeProvider
    {
        /// <summary>Gets the generated shape for specified type.</summary>
#nullable disable annotations // Use nullable-oblivious property type
        public global::PolyType.Abstractions.ITypeShape<string> String => _String ??= Create_String();
#nullable enable annotations // Use nullable-oblivious property type
        private global::PolyType.Abstractions.ITypeShape<string>? _String;

        private global::PolyType.Abstractions.ITypeShape<string> Create_String()
        {
            return new global::PolyType.SourceGenModel.SourceGenObjectTypeShape<string>
            {
                Provider = this,
                IsRecordType = false,
                IsTupleType = false,
                CreatePropertiesFunc = null,
                CreateConstructorFunc = null,
            };
        }
    }
}

Code and pdf at

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

RSCG – Datacute.EmbeddedResourcePropertyGenerator

RSCG – Datacute.EmbeddedResourcePropertyGenerator
 
 

name Datacute.EmbeddedResourcePropertyGenerator
nuget https://www.nuget.org/packages/Datacute.EmbeddedResourcePropertyGenerator/
link https://github.com/datacute/EmbeddedResourcePropertyGenerator/
author Stephen Denne

Generating class to access easy the embedded resource

 

This is how you can use Datacute.EmbeddedResourcePropertyGenerator .

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>
    <EmbeddedResource Include="TestData\Countries.txt" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Datacute.EmbeddedResourcePropertyGenerator" Version="1.0.0" >
    </PackageReference>
  </ItemGroup>
	<PropertyGroup>
		<AdditionalFileItemNames>$(AdditionalFileItemNames);EmbeddedResource</AdditionalFileItemNames>
	</PropertyGroup>
	<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 EmbedDemo;

Console.WriteLine("Hello, World!");

var data= TestData.Countries;

Console.WriteLine(data);


using Datacute.EmbeddedResourcePropertyGenerator;
namespace EmbedDemo;
[EmbeddedResourceProperties(".txt", "TestData")]
public static partial class TestData;



USA
Germany
France
Romania
Italy


 

The code that is generated is

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by the Datacute.EmbeddedResourcePropertyGenerator.
// </auto-generated>
//------------------------------------------------------------------------------

#nullable enable

namespace EmbedDemo;
/// <summary>
/// This class's properties are generated from project files meeting the criteria:
/// <list type="bullet">
/// <item>
/// <description>they are both an <c>EmbeddedResource</c> and an <c>AdditionalFile</c></description>
/// </item>
/// <item>
/// <description>they are in the project folder <c>TestData</c></description>
/// </item>
/// <item>
/// <description>they have the extension <c>.txt</c></description>
/// </item>
/// </list>
/// </summary>
public static partial class TestData
{
    private static class EmbeddedResource
    {
        public static string Read(string resourceName)
        {
            var assembly = typeof(TestData).Assembly;
            using var stream = assembly.GetManifestResourceStream(resourceName)!;
            using var streamReader = new global::System.IO.StreamReader(stream, global::System.Text.Encoding.UTF8);
            var resourceText = streamReader.ReadToEnd();
            return resourceText;
        }
        public static class BackingField
        {
            public static string? Countries;
        }
        public static class ResourceName
        {
            public const string Countries = "EmbedDemo.TestData.Countries.txt";
        }
    }
    static partial void ReadEmbeddedResourceValue(ref string? backingField, string resourceName, string propertyName);
    static partial void AlterEmbeddedResourceReturnValue(ref string value, string resourceName, string propertyName);

    /// <summary>Text value of the Embedded Resource: Countries.txt</summary>
    /// <value>
    /// <code>
    /// USA
    /// Germany
    /// France
    /// Romania
    /// Italy
    /// 
    /// </code>
    /// </value>
    /// <remarks>
    /// The value is read from the embedded resource on first access.
    /// </remarks>
    public static string Countries
    {
        get
        {
            ReadEmbeddedResourceValue(ref EmbeddedResource.BackingField.Countries, EmbeddedResource.ResourceName.Countries, "Countries");
            var value = EmbeddedResource.BackingField.Countries ??= EmbeddedResource.Read(EmbeddedResource.ResourceName.Countries);
            AlterEmbeddedResourceReturnValue(ref value, EmbeddedResource.ResourceName.Countries, "Countries");
            return value;
        }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Datacute.EmbeddedResourcePropertyGenerator

RSCG – rscg_queryables

RSCG – rscg_queryables
 
 

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

Generating code for .Where and .OrderBy by string, not by lambda

 

This is how you can use rscg_queryables .

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


The code that you will use is


using SortAndWhere;


Console.WriteLine("Hello, World!");

var students = new Student[]
{
    new Student { FirstName = "John", LastName = "Doe", StartYear = 1935},
    new Student { FirstName = "Ignat", LastName = "Andrei", StartYear = 1989 },
};

var orderedExplicitly = students.OrderBy(p => p.FirstName).ToArray();
var orderedImplicitly = students.OrderBy("firStnaMe").ToArray();
var orderedImplicitly2 = students.AsQueryable().OrderBy("fIrsTnAme").ToArray();


//Search by property name

var search = students.AsQueryable().Where("firstName", rscg_queryablesCommon.WhereOperator.Equal, "John").ToArray();

Console.WriteLine("found : " + search.Length);

search = students.AsQueryable().Where(Student_.Where_Expr("firstName", rscg_queryablesCommon.WhereOperator.Equal, "John")).ToArray();
Console.WriteLine("found : " + search.Length);





using rscg_queryablesCommon;

namespace SortAndWhere;
[MakeSortable]
[MakeWhere]
public class Student
{
    public string FirstName { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;

    public int StartYear { get; set; }

}


 

The code that is generated is


public static partial class ExtensionsSortable_SortAndWhere_Student {
    
    #region Enumerable
    public static System.Linq.IOrderedEnumerable<global::SortAndWhere.Student> OrderBy
    (
    this IEnumerable<global::SortAndWhere.Student>
        source, string propertyName
    )
    {

        return OrderByAscDesc(source,propertyName,true);
    }
        public static System.Linq.IOrderedEnumerable<global::SortAndWhere.Student>
            OrderByDescending
            (
            this IEnumerable<global::SortAndWhere.Student>
                source, string propertyName
                )
                {

                return OrderByAscDesc(source,propertyName,false);
                }
    public static System.Linq.IOrderedEnumerable<global::SortAndWhere.Student> OrderByAscDesc
    (
     this IEnumerable<global::SortAndWhere.Student> source, string propertyName, bool Ascending
    )
   {

                        if(string.Equals(propertyName, "FirstName", StringComparison.OrdinalIgnoreCase)){
                            if(Ascending)
                                return source.OrderBy(x => x.FirstName);
                            else
                                return source.OrderByDescending(x => x.FirstName);                            
                        }
                    
                        if(string.Equals(propertyName, "LastName", StringComparison.OrdinalIgnoreCase)){
                            if(Ascending)
                                return source.OrderBy(x => x.LastName);
                            else
                                return source.OrderByDescending(x => x.LastName);                            
                        }
                    
                        if(string.Equals(propertyName, "StartYear", StringComparison.OrdinalIgnoreCase)){
                            if(Ascending)
                                return source.OrderBy(x => x.StartYear);
                            else
                                return source.OrderByDescending(x => x.StartYear);                            
                        }
                                    throw new ArgumentException($"Property {propertyName} not found", propertyName);
    }

    public static System.Linq.IOrderedEnumerable<global::SortAndWhere.Student> ThenByAscDesc
    (
     this IOrderedEnumerable<global::SortAndWhere.Student> source, string propertyName, bool Ascending
    )
   {

                        if(string.Equals(propertyName, "FirstName", StringComparison.OrdinalIgnoreCase)){
                            if(Ascending)
                                return source.ThenBy(x => x.FirstName);
                            else
                                return source.ThenByDescending(x => x.FirstName);                            
                        }
                    
                        if(string.Equals(propertyName, "LastName", StringComparison.OrdinalIgnoreCase)){
                            if(Ascending)
                                return source.ThenBy(x => x.LastName);
                            else
                                return source.ThenByDescending(x => x.LastName);                            
                        }
                    
                        if(string.Equals(propertyName, "StartYear", StringComparison.OrdinalIgnoreCase)){
                            if(Ascending)
                                return source.ThenBy(x => x.StartYear);
                            else
                                return source.ThenByDescending(x => x.StartYear);                            
                        }
                                    throw new ArgumentException($"Property {propertyName} not found", propertyName);
    }
    public static System.Linq.IOrderedEnumerable<global::SortAndWhere.Student> ThenBy
    (
    this IOrderedEnumerable<global::SortAndWhere.Student>
        source, string propertyName
    )
    {

        return ThenByAscDesc(source,propertyName,true);
    }
    public static System.Linq.IOrderedEnumerable<global::SortAndWhere.Student> ThenByDescending
    (
    this IOrderedEnumerable<global::SortAndWhere.Student>
        source, string propertyName
    )
    {

        return ThenByAscDesc(source,propertyName,false);
    }

    #endregion 

#region Queryable
    public static System.Linq.IOrderedQueryable<global::SortAndWhere.Student> OrderBy
    (
    this IQueryable<global::SortAndWhere.Student>
        source, string propertyName
    )
    {

        return OrderByAscDesc(source,propertyName,true);
    }
        public static System.Linq.IOrderedQueryable<global::SortAndWhere.Student>
            OrderByDescending
            (
            this IQueryable<global::SortAndWhere.Student>
                source, string propertyName
                )
                {

                return OrderByAscDesc(source,propertyName,false);
                }
    public static System.Linq.IOrderedQueryable<global::SortAndWhere.Student> OrderByAscDesc
    (
     this IQueryable<global::SortAndWhere.Student> source, string propertyName, bool Ascending
    )
   {

                        if(string.Equals(propertyName, "FirstName", StringComparison.OrdinalIgnoreCase)){
                            if(Ascending)
                                return source.OrderBy(x => x.FirstName);
                            else
                                return source.OrderByDescending(x => x.FirstName);                            
                        }
                    
                        if(string.Equals(propertyName, "LastName", StringComparison.OrdinalIgnoreCase)){
                            if(Ascending)
                                return source.OrderBy(x => x.LastName);
                            else
                                return source.OrderByDescending(x => x.LastName);                            
                        }
                    
                        if(string.Equals(propertyName, "StartYear", StringComparison.OrdinalIgnoreCase)){
                            if(Ascending)
                                return source.OrderBy(x => x.StartYear);
                            else
                                return source.OrderByDescending(x => x.StartYear);                            
                        }
                                    throw new ArgumentException($"Property {propertyName} not found", propertyName);
    }
    public static System.Linq.IOrderedQueryable<global::SortAndWhere.Student> ThenByAscDesc
    (
     this IOrderedQueryable<global::SortAndWhere.Student> source, string propertyName, bool Ascending
    )
   {

                        if(string.Equals(propertyName, "FirstName", StringComparison.OrdinalIgnoreCase)){
                            if(Ascending)
                                return source.ThenBy(x => x.FirstName);
                            else
                                return source.ThenByDescending(x => x.FirstName);                            
                        }
                    
                        if(string.Equals(propertyName, "LastName", StringComparison.OrdinalIgnoreCase)){
                            if(Ascending)
                                return source.ThenBy(x => x.LastName);
                            else
                                return source.ThenByDescending(x => x.LastName);                            
                        }
                    
                        if(string.Equals(propertyName, "StartYear", StringComparison.OrdinalIgnoreCase)){
                            if(Ascending)
                                return source.ThenBy(x => x.StartYear);
                            else
                                return source.ThenByDescending(x => x.StartYear);                            
                        }
                                    throw new ArgumentException($"Property {propertyName} not found", propertyName);
    }
    public static System.Linq.IOrderedQueryable<global::SortAndWhere.Student> ThenBy
    (
    this IOrderedQueryable<global::SortAndWhere.Student>
        source, string propertyName
    )
    {

        return ThenByAscDesc(source,propertyName,true);
    }
    public static System.Linq.IOrderedQueryable<global::SortAndWhere.Student> ThenByDescending
    (
    this IOrderedQueryable<global::SortAndWhere.Student>
        source, string propertyName
    )
    {

        return ThenByAscDesc(source,propertyName,false);
    }

    #endregion 

}

public static class ExtensionsWhere_SortAndWhere_Student{
    

        internal static System.Linq.Expressions.Expression < Func < global::SortAndWhere.Student,bool>>
        FirstName_Expr_Equal(string argument) => (a => a.FirstName== argument);

        internal static Func<global::SortAndWhere.Student,bool>
            FirstName_Equal(string argument) => (a => a.FirstName== argument);

        internal static System.Linq.Expressions.Expression < Func < global::SortAndWhere.Student,bool>>
        FirstName_Expr_NotEqual(string argument) => (a => a.FirstName != argument);

        internal static Func<global::SortAndWhere.Student,bool>
                FirstName_NotEqual(string argument) => (a => a.FirstName != argument);

    
        internal static System.Linq.Expressions.Expression < Func < global::SortAndWhere.Student,bool>>
        LastName_Expr_Equal(string argument) => (a => a.LastName== argument);

        internal static Func<global::SortAndWhere.Student,bool>
            LastName_Equal(string argument) => (a => a.LastName== argument);

        internal static System.Linq.Expressions.Expression < Func < global::SortAndWhere.Student,bool>>
        LastName_Expr_NotEqual(string argument) => (a => a.LastName != argument);

        internal static Func<global::SortAndWhere.Student,bool>
                LastName_NotEqual(string argument) => (a => a.LastName != argument);

    
        internal static System.Linq.Expressions.Expression < Func < global::SortAndWhere.Student,bool>>
        StartYear_Expr_Equal(int argument) => (a => a.StartYear== argument);

        internal static Func<global::SortAndWhere.Student,bool>
            StartYear_Equal(int argument) => (a => a.StartYear== argument);

        internal static System.Linq.Expressions.Expression < Func < global::SortAndWhere.Student,bool>>
        StartYear_Expr_NotEqual(int argument) => (a => a.StartYear != argument);

        internal static Func<global::SortAndWhere.Student,bool>
                StartYear_NotEqual(int argument) => (a => a.StartYear != argument);

    
   

}
public static class Student_{


        public static System.Linq.IQueryable<global::SortAndWhere.Student>
            Where(this System.Linq.IQueryable<global::SortAndWhere.Student> original,
                string propertyName,rscg_queryablesCommon.WhereOperator operatorWhere, string argument)
        {
            return original.Where(Where_Expr(propertyName, operatorWhere, argument));
        }
        public static System.Linq.Expressions.Expression < Func < global::SortAndWhere.Student,bool>>
        Where_Expr(string propertyName,rscg_queryablesCommon.WhereOperator operatorWhere, string argument)
            {

                    if(string.Equals(propertyName,  "FirstName", StringComparison.OrdinalIgnoreCase)){
                    switch(operatorWhere){
                    case rscg_queryablesCommon.WhereOperator.Equal:
                    return ExtensionsWhere_SortAndWhere_Student.FirstName_Expr_Equal( argument);
                    case rscg_queryablesCommon.WhereOperator.NotEqual:
                    return ExtensionsWhere_SortAndWhere_Student.FirstName_Expr_NotEqual( argument);
                    default:
                    throw new ArgumentException($"Operator {operatorWhere} not found");
                    }//end switch
                    }//end if FirstName

                
                    if(string.Equals(propertyName,  "LastName", StringComparison.OrdinalIgnoreCase)){
                    switch(operatorWhere){
                    case rscg_queryablesCommon.WhereOperator.Equal:
                    return ExtensionsWhere_SortAndWhere_Student.LastName_Expr_Equal( argument);
                    case rscg_queryablesCommon.WhereOperator.NotEqual:
                    return ExtensionsWhere_SortAndWhere_Student.LastName_Expr_NotEqual( argument);
                    default:
                    throw new ArgumentException($"Operator {operatorWhere} not found");
                    }//end switch
                    }//end if LastName

                
            throw new ArgumentException("Property "+ propertyName +" not found for string type");
            }





        public static Func<global::SortAndWhere.Student,bool>
            Where(string propertyName,rscg_queryablesCommon.WhereOperator operatorWhere, string argument)
            {

                    if(string.Equals(propertyName,  "FirstName", StringComparison.OrdinalIgnoreCase)){
                    switch(operatorWhere){
                    case rscg_queryablesCommon.WhereOperator.Equal:
                    return ExtensionsWhere_SortAndWhere_Student.FirstName_Equal( argument);
                    case rscg_queryablesCommon.WhereOperator.NotEqual:
                    return ExtensionsWhere_SortAndWhere_Student.FirstName_NotEqual( argument);
                    default:
                    throw new ArgumentException($"Operator {operatorWhere} not found");
                    }//end switch
                    }//end if FirstName

                
                    if(string.Equals(propertyName,  "LastName", StringComparison.OrdinalIgnoreCase)){
                    switch(operatorWhere){
                    case rscg_queryablesCommon.WhereOperator.Equal:
                    return ExtensionsWhere_SortAndWhere_Student.LastName_Equal( argument);
                    case rscg_queryablesCommon.WhereOperator.NotEqual:
                    return ExtensionsWhere_SortAndWhere_Student.LastName_NotEqual( argument);
                    default:
                    throw new ArgumentException($"Operator {operatorWhere} not found");
                    }//end switch
                    }//end if LastName

                
            throw new ArgumentException("Property "+ propertyName +" not found for string type");
            }

    
        public static System.Linq.IQueryable<global::SortAndWhere.Student>
            Where(this System.Linq.IQueryable<global::SortAndWhere.Student> original,
                string propertyName,rscg_queryablesCommon.WhereOperator operatorWhere, int argument)
        {
            return original.Where(Where_Expr(propertyName, operatorWhere, argument));
        }
        public static System.Linq.Expressions.Expression < Func < global::SortAndWhere.Student,bool>>
        Where_Expr(string propertyName,rscg_queryablesCommon.WhereOperator operatorWhere, int argument)
            {

                    if(string.Equals(propertyName,  "StartYear", StringComparison.OrdinalIgnoreCase)){
                    switch(operatorWhere){
                    case rscg_queryablesCommon.WhereOperator.Equal:
                    return ExtensionsWhere_SortAndWhere_Student.StartYear_Expr_Equal( argument);
                    case rscg_queryablesCommon.WhereOperator.NotEqual:
                    return ExtensionsWhere_SortAndWhere_Student.StartYear_Expr_NotEqual( argument);
                    default:
                    throw new ArgumentException($"Operator {operatorWhere} not found");
                    }//end switch
                    }//end if StartYear

                
            throw new ArgumentException("Property "+ propertyName +" not found for int type");
            }





        public static Func<global::SortAndWhere.Student,bool>
            Where(string propertyName,rscg_queryablesCommon.WhereOperator operatorWhere, int argument)
            {

                    if(string.Equals(propertyName,  "StartYear", StringComparison.OrdinalIgnoreCase)){
                    switch(operatorWhere){
                    case rscg_queryablesCommon.WhereOperator.Equal:
                    return ExtensionsWhere_SortAndWhere_Student.StartYear_Equal( argument);
                    case rscg_queryablesCommon.WhereOperator.NotEqual:
                    return ExtensionsWhere_SortAndWhere_Student.StartYear_NotEqual( argument);
                    default:
                    throw new ArgumentException($"Operator {operatorWhere} not found");
                    }//end switch
                    }//end if StartYear

                
            throw new ArgumentException("Property "+ propertyName +" not found for int type");
            }

    

}

Code and pdf at

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

Watch2–part7–full test

Watch2 NuGet Package

The Watch2 NuGet package extends the functionality of dotnet watch by adding features such as console clearing and delay handling. It outputs the same information as dotnet watch.

1. Wrapper Interfaces

To facilitate testing, it is necessary to wrap various process creation classes from .NET (e.g., Process => ProcessWrapper, ProcessStartInfo => ProcessStartInfoWrapper). This allows for the testing of process outputs. These wrappers are primarily interfaces that can be mocked and redirected to the main class.

Most of these wrappers can be generated automatically using a Roslyn Code Generator.

Rule of Thumb

If the main class you want to wrap contains properties, methods, or events that return or accept instances of another class, you should create a wrapper for that class as well.

Example 1

The Process class has a property StartInfo of type ProcessStartInfo. Therefore, a wrapper for ProcessStartInfo must be created.

Example 2

The Process class has an event OutputDataReceived of type DataReceivedEventArgs. Consequently, a wrapper for DataReceivedEventArgs is required.

2. Rocks and Waiting for an Async Call

When mocking an async method with Rocks, it is essential to wait for the async call to complete.

For example, you must wait for .WaitForExitAsync() to finish before checking the process output.

Watch2–part 6- options

The Watch2 package passes all the command line arguments to the dotnet watch. So how can it have its own settings? Simple: by reading a watch.json file that is in the same folder where it is executing.

1. Testing
I was considering how to perform the testing, and there were two options:

Microsoft File Providers: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/file-providers?view=aspnetcore-8.0

TestableIO: https://github.com/TestableIO/System.IO.Abstractions

I thought that the second option (TestableIO) would be easier to use and more flexible. However, I do not need to test the creation of the file, but rather the content of the file. Therefore, I will use the first option (Microsoft File Providers).

2. Maintain JSON in Sync with C#
I need a solution to transform JSON to C# on the fly (i.e., at compile time). For this, a Roslyn Code Generator can be used. The rscgutils NuGet package will be helpful for this purpose.

Here is the relevant configuration in the project file:

<ItemGroup>
    <PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="8.0.0" />
    <PackageReference Include="rscgutils" Version="2024.2000.2000" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

<ItemGroup>
    <ProjectReference Include="..\Watch2_Interfaces\Watch2_Interfaces.csproj" />
</ItemGroup>
<ItemGroup>
	<AdditionalFiles Include="options.gen.json" />
</ItemGroup>

Code in Program.cs
The code in Program.cs will utilize the above configurations and packages. Here is an example of how it might look:

This example demonstrates how to read the watch.json file using the Microsoft File Providers package. The rscgutils package will handle the transformation of JSON to C# at compile time, ensuring that the JSON configuration is always in sync with the C# code.

So the code in program.cs will be:

//test
//args = ["run --no-hot-reload"];
//string folder = @"D:\gth\RSCG_Examples\v2\Generator";

//uncomment this line for production
string folder = Environment.CurrentDirectory;

var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection, folder);

var serviceProvider = serviceCollection.BuildServiceProvider();

var console = serviceProvider.GetRequiredService<IConsoleWrapper>();
var processManager = serviceProvider.GetRequiredService<ProcessManager>();
var startInfo = serviceProvider.GetRequiredService<IProcessStartInfo>();

var fileOptions = serviceProvider.GetRequiredService<IOptionsReader>();
if(!fileOptions.ExistsFile())
{
    var file = Path.Combine(folder, "watch2.json");
    File.WriteAllText(file, MyAdditionalFiles.options_gen_json);    
}

await processManager.StartProcessAsync(args, console, startInfo);



void ConfigureServices(IServiceCollection services,string folder)
{
    services.AddSingleton<IFileProvider>(new PhysicalFileProvider(folder));
    services.AddSingleton<IOptionsReader, OptionsReader>();
    services.AddSingleton<Ioptions_gen_json>(it =>
    {
        var optionsReader = it.GetRequiredService<IOptionsReader>();
        return optionsReader.GetOptions() ?? options_gen_json.Empty;
    });
    services.AddSingleton<IConsoleWrapper, ConsoleWrapper>();
    services.AddSingleton<ProcessManager, ProcessManager>();
    services.AddSingleton<IProcessStartInfo>(provider => new ProcessStartInfoWrapper
    {
    FileName = "dotnet",
    Arguments = "watch " + string.Join(' ', args),
    WorkingDirectory = folder,
    RedirectStandardOutput = true,
    RedirectStandardInput = true,
    RedirectStandardError = true,
    UseShellExecute = false,
    CreateNoWindow = true
    });

    services.AddLogging(loggingBuilder =>
    {
        loggingBuilder.ClearProviders();
        loggingBuilder.SetMinimumLevel(LogLevel.Trace);
        loggingBuilder.AddNLog("nlog.config");
    });
    services.AddSingleton<ILogger<ProcessManager>, Logger<ProcessManager>>();
    services.AddSingleton<Func<IProcessStartInfo, IProcessWrapper>>(it => new ProcessWrapper(it));
}

Watch2- part 5–DI

The Watch2 NuGet package project now has interfaces, making it test-friendly and ready for action.

To make life easier, we’ve sprinkled some Dependency Injection (DI) magic throughout the project.

Classes have been refactored to embrace constructor injection, ensuring all necessary interfaces are on board.

The DI container has been set up in `Program.cs`, making dependency resolution a breeze. The tests have also been given a makeover to fit the new DI style.

This is a never-ending journey, but hey, it’s a pretty good start!

The program.cs now looks like this:



var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);

var serviceProvider = serviceCollection.BuildServiceProvider();

var console = serviceProvider.GetRequiredService<IConsoleWrapper>();
var processManager = serviceProvider.GetRequiredService<ProcessManager>();
var startInfo = serviceProvider.GetRequiredService<IProcessStartInfo>();

await processManager.StartProcessAsync(args, console, startInfo);

void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConsoleWrapper, ConsoleWrapper>();
services.AddSingleton<ProcessManager, ProcessManager>();
services.AddSingleton<IProcessStartInfo>(provider => new ProcessStartInfoWrapper
{
FileName = "dotnet",
Arguments = "watch " + string.Join(' ', args),
WorkingDirectory = Environment.CurrentDirectory,
RedirectStandardOutput = true,
RedirectStandardInput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
});

    services.AddLogging(loggingBuilder =>
    {
        loggingBuilder.ClearProviders();
        loggingBuilder.SetMinimumLevel(LogLevel.Trace);
        loggingBuilder.AddNLog("nlog.config");
    });
}

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.