Category: roslyn

Templating Roslyn Source Code Generators

I want that , when I generate code with Roslyn, to have a template that I can easy modify to generate code . Also, I want to let the user ( the programmer , in this case) modify this template  – maybe he has better ideas than me.

For reading from the RSCG, is pretty simple: Make as an embedded resource and read with

internal class EmbedReader
{
     static Assembly assembly;
     static EmbedReader()
     {
         assembly = Assembly.GetExecutingAssembly();

    }
     public static string ContentFile(string name)
     {
         var resourceName = name;

        using (Stream stream = assembly.GetManifestResourceStream(resourceName))
         using (StreamReader reader = new StreamReader(stream))
         {
             string result = reader.ReadToEnd();
             //this is the content
         }
     }
}

For let the programmer user put his template, put as as additional file in the csproj

<ItemGroup>
         <AdditionalFiles Include=”context.txt”  />

</ItemGroup>

and read with

var file = context.AdditionalFiles.Where(it => it.Path.EndsWith($”{val}.txt”))
                     .Select(it => it.GetText())
                     .FirstOrDefault();

            if (file != null)
             {
                 content = string.Join(Environment.NewLine, file.Lines.Select(it => it.ToString()));
             }

To do both,if the result of the first is empty, read the second (or opposite ? )

[RSCG]–About My Software

I have made a new improvement to my Roslyn Source Code Generator, AMS . Until now it just said the time and the commit date in a CI scenario  ( Git ( GitLab, GitHub), Azure …)

Now –  what if it can list all the merge requests between 2 dates  , so you can see what is new ? 

Now you can have – see this:

What you need to do  ?

Just add the following codes

using AMS_Base;
[assembly:VersionReleased(Name=”PreviousReleases”,ISODateTime =”2022-03-31″,recordData = RecordData.Merges)]
[assembly: VersionReleased(Name = “WithVersioning”, ISODateTime = “2022-04-02”, recordData = RecordData.Merges)]
[assembly: AMS_Base.VersionReleased(Name = “FutureRelease”, ISODateTime = “9999-04-16”, recordData = AMS_Base.RecordData.Merges)]

( Of course , you should read first how to add the NuGet package – see https://github.com/ignatandrei/RSCG_AMS/blob/main/README.md )

How it is made  ?

First, we should find Git –  start a process with

Where git.exe

or

which git

to find the location.

Second, start a process with the location of the git find below

log –merges –pretty=””%an|%cs|%H|%s

to see the merges . Then merge with the dates

assembly:VersionReleased 

that are registered before – and this is all !

FileExtension– export data from CSV–part 2

First, is how to export data from table (https://en.wikipedia.org/wiki/List_of_file_signatures) into classes . First, the table must be transformed into a more programmatic recognizable way – like a CSV  – see https://github.com/ignatandrei/FileExtension/blob/master/src/RSCG_GCK/offset0.txt

Then a solution is to have into the dll ( as embedded resource or as a file) . The second solution, more complicated , is to use Roslyn Source Code Generators to generate the classes for each element of the CSV .

I’d liked more the second solution – the sources are at https://github.com/ignatandrei/FileExtension/tree/master/src/RSCG_GCK 

What it generates is like this

namespace RecognizeCustomSigs_GCK {
    class RecognizeFromGCKLine0_JP2: RecognizeFromLineCustomsigs{
        public RecognizeFromGCKLine0_JP2(): base("JPEG2000 image files,00 00 00 0C 6A 50 20 20,JP2")                
        {
        }
    }
}            

Evolving Roslyn Source Code Generators

I am a big fan of Roslyn Source Code Generators – you can read about at https://github.com/dotnet/roslyn/blob/main/docs/features/source-generators.md and https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/. I have tested , until now, 27 examples and discovered other 53 RSCG – you can read about at https://github.com/ignatandrei/RSCG_Examples and download the book from  https://ignatandrei.github.io/RSCG_Examples/

Also, Microsoft is dogfooding this by using at Blazor ( https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-2/#razor-compiler-updated-to-use-source-generators ), JSON.NET https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-source-generator/ . This will alleviate some of the pain points from https://turnerj.com/blog/the-pain-points-of-csharp-source-generators .

Now Microsoft is evolving the specification . I have read about Incremental Generators – https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md . This sounds promising – and the fact that is called “Incremental” , that can generate more than source code and can be cached sounds great. Let’s see !

RSCG Example – AOPMarker for CI Builds – part 30

 

 

name AOPMarkerCI
nuget

https://www.nuget.org/packages/AOPMethodsCommon/
https://www.nuget.org/packages/AOPMethodsGenerator/

link http://msprogrammer.serviciipeweb.ro/category/roslyn/
author Andrei Ignat

This will tracing methods marked with AOPMarkerMethod in CI builds. Does not affect the code run by the programmer.
 

The code that you start with is


    using AOPMethodsCommon;

    using System;

    using System.Threading.Tasks;

    

    namespace AOPMarkerCI

    {

        [AutoMethods(template = TemplateMethod.CustomTemplateFile, MethodPrefix = "auto", CustomTemplateFileName = "../AutoMethod.txt")]

        partial class UnderTest

        {

            [AOPMarkerMethod]

            public async Task<int> Method1()

            {

                await Task.Delay(1000);

                var ret = Method2(DateTime.Now);

                return ret % 2 == 0 ? 1 : 0;

            }

            [AOPMarkerMethod]

            private int Method2(DateTime now)

            {

                return now.Second;

            }

    

        }

    }


The code that you will use is



    Console.WriteLine("Run the autoci file");

    var underTest = new UnderTest();

    int i = await underTest.Method1();

    Console.WriteLine($"result:{i}");

 

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>

    //------------------------------------------------------------------------------

    using System;

    using System.Collections.Generic;

    using System.CodeDom.Compiler;

    using System.Runtime.CompilerServices;

    using System.Diagnostics;

    namespace AOPMarkerCI {

    

        [GeneratedCode("AOPMethods", "2021.6.11.907")]

        [CompilerGenerated]

        public partial class UnderTest{

            //autoMethod1

                

            public  async  System.Threading.Tasks.Task<int> Method1 (){

    

         Console.WriteLine("start method autoMethod1 at " +utcTime);

    

                try{

                     return   await  autoMethod1();

                }

                catch(Exception ex){

                    Console.WriteLine($"--autoMethod1 exception {ex.Message}");

                    throw;

                }

                finally{

                        utcTime =System.DateTime.UtcNow;

                    Console.WriteLine("end method autoMethod1");                

                }

    

    

            }//end Method1

            

                    //autoMethod2

                

            public  int Method2 (System.DateTime now){

                   var utcTime =System.DateTime.UtcNow;

                 

                   Console.WriteLine("start method autoMethod2 at " +utcTime);

                    string valnow ;

                    try{

                        valnow = System.Text.Json.JsonSerializer.Serialize(now);

                    }

                    catch(Exception ex){

                        valnow = "Error serializing parameter now : "+ ex.Message;

                    }

                    Console.WriteLine("Argument_now :" + valnow);

                    

                 

                

    

                try{

                     return   autoMethod2(now);

                }

                catch(Exception ex){

                    Console.WriteLine($"--autoMethod2 exception {ex.Message}");

                    throw;

                }

                finally{

                        utcTime =System.DateTime.UtcNow;

                    Console.WriteLine("end method autoMethod2");                

                }

    

    

            }//end Method2

            

            

        }

     }

    

    

Example Code: https://github.com/ignatandrei/RSCG_Examples/tree/main/AOPMarkerCI

All RSCG

NrBlog Post
1RSCG–part 1
2RSCG- AppVersion–part 2
3http://msprogrammer.serviciipeweb.ro/2021/02/17/rsgc-enum-part-3/
4RSGC-JSON to Class- part 4
5RSGC-Constructor – Deconstructor – part 5
6RSGC – DTO Mapper – part 6
7RSGC – Skinny Controllers- part 7
8RSGC-Builder Design Pattern – part 8
9RSGC- MetadataFromObject – part 9
10RSGC- Dynamic Mock – part 10
11RSCG- Method Decorator – part 11
12RSCG – Curry – Partial function – part 12
13RSCG- part 13 – IFormattable
14RSCG- part 14 – DP_Decorator
15RSCG- part 15 – Expression Generator
16RSCG- part 16 – Many Others
17RSCG- the book
18RSCG–Template Rendering- part 17
19CI Version
20HttpClientGenerator
21Query from database
22AutoRegister
23TinyTypes
24Static2Interface
25AppSettings
26Properties
27
Roslyn Source Code Generators

RSCG Example – Class2Interface – part 29

 

 

name BoilerplateFree
nuget

https://www.nuget.org/packages/boilerplatefree

link https://github.com/GeeWee/boilerplatefree
author Gustav Wengel

This will generate interface from a class
 

The code that you start with is


    [AutoGenerateInterface]                        

    public partial class Person: IPerson

    {

        public void Foo()

        {

            Console.WriteLine("Foo");

        }

        //dummy

        private string s { get; set; }

        public int ID { get; set; }

        public string Name { get; set; }

        //dummy

        public static string NewID { get; set; }

    }


The code that you will use is



    IPerson p = new Person();

    p.Name = "Andrei";

    p.Foo();

    Console.WriteLine(p.Name);

 

The code that is generated is


    public interface IPerson {

    public void Foo();

    public int  ID  {get; set; }

    public string  Name  {get; set; }

    }

Example Code: https://github.com/ignatandrei/RSCG_Examples/tree/main/Class2Interface

All RSCG

NrBlog Post
1RSCG–part 1
2RSCG- AppVersion–part 2
3http://msprogrammer.serviciipeweb.ro/2021/02/17/rsgc-enum-part-3/
4RSGC-JSON to Class- part 4
5RSGC-Constructor – Deconstructor – part 5
6RSGC – DTO Mapper – part 6
7RSGC – Skinny Controllers- part 7
8RSGC-Builder Design Pattern – part 8
9RSGC- MetadataFromObject – part 9
10RSGC- Dynamic Mock – part 10
11RSCG- Method Decorator – part 11
12RSCG – Curry – Partial function – part 12
13RSCG- part 13 – IFormattable
14RSCG- part 14 – DP_Decorator
15RSCG- part 15 – Expression Generator
16RSCG- part 16 – Many Others
17RSCG- the book
18RSCG–Template Rendering- part 17
19CI Version
20HttpClientGenerator
21Query from database
22AutoRegister
23TinyTypes
24Static2Interface
25AppSettings
26Properties
27
Roslyn Source Code Generators

RSCG Example – ToStringDebugger – part 28

 

 

name StructRecordsGenerator
nuget

https://www.nuget.org/packages/StructRecordGenerator/

link https://github.com/SergeyTeplyakov/StructRecordsGenerator
author Sergey Teplyakov

This will generate code .ToString. Usefull for debugging
 

The code that you start with is


    [StructGenerators.GenerateToString(PrintTypeName = true)]

    class Person

    {

        [Required]

        public string FirstName { get; set; }

        public string LastName { get; set; }

    }


The code that you will use is



    var p = new Person();

    p.FirstName = "Andrei";

    //put here a debug watch to see p

    Console.WriteLine(p.ToString());

 

The code that is generated is


    partial class Person

    {

        /// <inheritdoc/>

        public override string ToString()

        {

            var sb = new StringBuilder();

            sb.Append("Person ");

            sb.Append("{ ");

            if (PrintMembers(sb))

            {

                sb.Append(" ");

            }

    

            sb.Append("}");

            return sb.ToString(0, Math.Min(sb.Length, /*String rep limit*/ 1024));

        }

    

        /// <summary>

        /// Prints the content of the instance into a given string builder.

        /// </summary>

        protected virtual bool PrintMembers(StringBuilder sb)

        {

            sb.Append("s = ");

            sb.Append((object)s);

            sb.Append(", ");

            sb.Append("ID = ");

            sb.Append(ID);

            sb.Append(", ");

            sb.Append("FirstName = ");

            sb.Append((object)FirstName);

            sb.Append(", ");

            sb.Append("LastName = ");

            sb.Append((object)LastName);

            return true;

        }

    }

Example Code: https://github.com/ignatandrei/RSCG_Examples/tree/main/DebuggerToString

All RSCG

NrBlog Post
1RSCG–part 1
2RSCG- AppVersion–part 2
3http://msprogrammer.serviciipeweb.ro/2021/02/17/rsgc-enum-part-3/
4RSGC-JSON to Class- part 4
5RSGC-Constructor – Deconstructor – part 5
6RSGC – DTO Mapper – part 6
7RSGC – Skinny Controllers- part 7
8RSGC-Builder Design Pattern – part 8
9RSGC- MetadataFromObject – part 9
10RSGC- Dynamic Mock – part 10
11RSCG- Method Decorator – part 11
12RSCG – Curry – Partial function – part 12
13RSCG- part 13 – IFormattable
14RSCG- part 14 – DP_Decorator
15RSCG- part 15 – Expression Generator
16RSCG- part 16 – Many Others
17RSCG- the book
18RSCG–Template Rendering- part 17
19CI Version
20HttpClientGenerator
21Query from database
22AutoRegister
23TinyTypes
24Static2Interface
25AppSettings
26Properties
27
Roslyn Source Code Generators

RSCG Example–TimeBombComment–part 27

 

 

name RSCG_TimeBombComment
nuget

https://www.nuget.org/packages/RSCG_TimeBombComment/

link http://msprogrammer.serviciipeweb.ro/category/roslyn/
author Andrei Ignat

This will generate an error from the comment after a certain date
 

The code that you start with is


    //TB: 2021-09-13 this is a comment transformed into an error

    //TB: and this is a warning

    //TB: 9999-12-30 and this will not appear

    Console.WriteLine("See the TB comment above ? ");


The code that you will use is



    //TB: yyyy-MM-dd this is a comment transformed into an error

 

The code that is generated is


    2>src\RSCG_TimeBombComment\Console_TimeBombComment\Program.cs(9,13,9,73): error TB: this is a comment transformed into an error

    2>src\RSCG_TimeBombComment\Console_TimeBombComment\Program.cs(10,13,10,40): warning TB: and this is a warning

Example Code: https://github.com/ignatandrei/RSCG_Examples/tree/main/TimeBombComment

All RSCG

NrBlog Post
1RSCG–part 1
2RSCG- AppVersion–part 2
3http://msprogrammer.serviciipeweb.ro/2021/02/17/rsgc-enum-part-3/
4RSGC-JSON to Class- part 4
5RSGC-Constructor – Deconstructor – part 5
6RSGC – DTO Mapper – part 6
7RSGC – Skinny Controllers- part 7
8RSGC-Builder Design Pattern – part 8
9RSGC- MetadataFromObject – part 9
10RSGC- Dynamic Mock – part 10
11RSCG- Method Decorator – part 11
12RSCG – Curry – Partial function – part 12
13RSCG- part 13 – IFormattable
14RSCG- part 14 – DP_Decorator
15RSCG- part 15 – Expression Generator
16RSCG- part 16 – Many Others
17RSCG- the book
18RSCG–Template Rendering- part 17
19CI Version
20HttpClientGenerator
21Query from database
22AutoRegister
23TinyTypes
24Static2Interface
25AppSettings
26Properties
27
Roslyn Source Code Generators

RSCG Example–ApparatusAOT–part 26

 

 

name ApparatusAOT
nuget

https://www.nuget.org/packages/Apparatus.AOT.Reflection/

link https://github.com/byme8/Apparatus.AOT.Reflection
author Stanislav Silin

This will generate code for investigating at runtime the properties of an object
 

The code that you start with is


    //no special requirements

    class Person

    {

        [Required]

        public string FirstName { get; set; }

        public string LastName { get; set; }

    }


The code that you will use is



    var pOldPerson = new Person();

    pOldPerson.FirstName = "Andrei";

    

    var prop = pOldPerson.GetProperties().Values ;

    foreach (var item in prop)

    {

        Console.WriteLine($"{item.Name} Attr: {item.Attributes.Length} value {item.Name}");

        if(item.TryGetValue(pOldPerson, out var val)){

            Console.WriteLine("value : " + val);

        }

        

    }            

 

The code that is generated is


    using System;

    using System.Linq;

    

    namespace Apparatus.AOT.Reflection

    {

        public static class CopyConstructor_PersonExtensions

        {

            [global::System.Runtime.CompilerServices.ModuleInitializer]

            public static void Bootstrap()

            {

                MetadataStore<global::CopyConstructor.Person>.Data = _lazy;

            }

    

            private static global::System.Lazy<global::System.Collections.Generic.IReadOnlyDictionary<string, IPropertyInfo>> _lazy = new global::System.Lazy<global::System.Collections.Generic.IReadOnlyDictionary<string, IPropertyInfo>>(new global::System.Collections.Generic.Dictionary<string, IPropertyInfo>

            {

                { "FirstName", new global::Apparatus.AOT.Reflection.PropertyInfo<global::CopyConstructor.Person,string>(

                            "FirstName", 

                            new global::System.Attribute[] 

                            {

                                new global::System.ComponentModel.DataAnnotations.RequiredAttribute(),

                            }, 

                            instance => instance.FirstName, (instance, value) => instance.FirstName = value)

                    },

                { "LastName", new global::Apparatus.AOT.Reflection.PropertyInfo<global::CopyConstructor.Person,string>(

                            "LastName", 

                            new global::System.Attribute[] 

                            {

                                

                            }, 

                            instance => instance.LastName, (instance, value) => instance.LastName = value)

                    },

            }); 

    

    

            internal static global::System.Collections.Generic.IReadOnlyDictionary<string, IPropertyInfo> GetProperties(this global::CopyConstructor.Person value)

            {

                return _lazy.Value;

            }

        }

    }

Example Code: https://github.com/ignatandrei/RSCG_Examples/tree/main/ApparatusAOT

All RSCG

NrBlog Post
1RSCG–part 1
2RSCG- AppVersion–part 2
3http://msprogrammer.serviciipeweb.ro/2021/02/17/rsgc-enum-part-3/
4RSGC-JSON to Class- part 4
5RSGC-Constructor – Deconstructor – part 5
6RSGC – DTO Mapper – part 6
7RSGC – Skinny Controllers- part 7
8RSGC-Builder Design Pattern – part 8
9RSGC- MetadataFromObject – part 9
10RSGC- Dynamic Mock – part 10
11RSCG- Method Decorator – part 11
12RSCG – Curry – Partial function – part 12
13RSCG- part 13 – IFormattable
14RSCG- part 14 – DP_Decorator
15RSCG- part 15 – Expression Generator
16RSCG- part 16 – Many Others
17RSCG- the book
18RSCG–Template Rendering- part 17
19CI Version
20HttpClientGenerator
21Query from database
22AutoRegister
23TinyTypes
24Static2Interface
25AppSettings
26Properties
27
Roslyn Source Code Generators

RSCG Example–AppSettings editor–part 25

 

 

name AppSettingsEditor
nuget

https://www.nuget.org/packages/appSettingsEditor/

link http://msprogrammer.serviciipeweb.ro/category/roslyn/
author Andrei Ignat

This will generate classes code from appsettings . Additionally , it generates API controller for editing and an UI interface
 

The code that you start with is


    {

      "Logging": {

        "LogLevel": {

          "Default": "Information",

          "Microsoft": "Warning",

          "Microsoft.Hosting.Lifetime": "Information"

        }

      },

      "AllowedHosts": "*"

    }


The code that you will use is



    endpoints.MapSettingsView<SettingsJson.appsettings>(Configuration);

 

The code that is generated is


    //------------------------------------------------------------------------------                                                                       

    // <auto-generated>

    //     This code was generated by a tool.

    //     Runtime Version:

    //

    //     Changes to this file may cause incorrect behavior and will be lost if

    //     the code is regenerated.

    // </auto-generated>

    //------------------------------------------------------------------------------

    using System;

    using System.Collections.Generic;

    using System.Runtime.Serialization;

    using Microsoft.Extensions.Configuration;

    using appSettingsEditor;

    namespace SettingsEditor.SettingsJson

    {

    

        //[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]

        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("appSettingsEditorAPI", "2021.3.21.2300")]

        public partial class LogLevel: IAppSettingsConfig<LogLevel> 

        {

            public object GetFromPropertyName(string propName, bool returnNull =false){

                

                propName=propName?.ToUpper();

                

                switch(propName){

                    

                    case "DEFAULT":

                        return this.Default ;

                    

                    case "MICROSOFT":

                        return this.Microsoft ;

                    

                    case "MICROSOFTHOSTINGLIFETIME":

                        return this.MicrosoftHostingLifetime ;

                    

                    default:

                        if(returnNull)

                            return null;

    

                        throw new ArgumentException("cannot found from LogLevel prop "+propName);            

    

                }

                

                

            }

    

            public IEnumerable<string> Properties(){

                

                    yield return "Default" ;

                

                    yield return "Microsoft" ;

                

                    yield return "MicrosoftHostingLifetime" ;

                

                yield break;

            }

            

            [System.Text.Json.Serialization.JsonPropertyName("Default")]

            public string Default { get; set; }

            

            [System.Text.Json.Serialization.JsonPropertyName("Microsoft")]

            public string Microsoft { get; set; }

            

            [System.Text.Json.Serialization.JsonPropertyName("Microsoft.Hosting.Lifetime")]

            public string MicrosoftHostingLifetime { get; set; }

            

            public  LogLevel LoadFromConfig(IConfiguration config)

            { 

                

                    config.GetSection("Logging.LogLevel").Bind(this);

                    return this;

                

            }

        }

    

        //[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]

        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("appSettingsEditorAPI", "2021.3.21.2300")]

        public partial class Logging: IAppSettingsConfig<Logging> 

        {

            public object GetFromPropertyName(string propName, bool returnNull =false){

                

                propName=propName?.ToUpper();

                

                switch(propName){

                    

                    case "LOGLEVEL":

                        return this.LogLevel ;

                    

                    default:

                        if(returnNull)

                            return null;

    

                        throw new ArgumentException("cannot found from Logging prop "+propName);            

    

                }

                

                

            }

    

            public IEnumerable<string> Properties(){

                

                    yield return "LogLevel" ;

                

                yield break;

            }

            

            [System.Text.Json.Serialization.JsonPropertyName("LogLevel")]

            public LogLevel LogLevel { get; set; }

            

            public  Logging LoadFromConfig(IConfiguration config)

            { 

                

                    config.GetSection("Logging").Bind(this);

                    return this;

                

            }

        }

    

        //[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]

        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("appSettingsEditorAPI", "2021.3.21.2300")]

        public partial class appsettings: IAppSettingsConfig<appsettings> 

        {

            public object GetFromPropertyName(string propName, bool returnNull =false){

                

                propName=propName?.ToUpper();

                

                switch(propName){

                    

                    case "LOGGING":

                        return this.Logging ;

                    

                    case "ALLOWEDHOSTS":

                        return this.AllowedHosts ;

                    

                    default:

                        if(returnNull)

                            return null;

    

                        throw new ArgumentException("cannot found from appsettings prop "+propName);            

    

                }

                

                

            }

    

            public IEnumerable<string> Properties(){

                

                    yield return "Logging" ;

                

                    yield return "AllowedHosts" ;

                

                yield break;

            }

            

            [System.Text.Json.Serialization.JsonPropertyName("Logging")]

            public Logging Logging { get; set; }

            

            [System.Text.Json.Serialization.JsonPropertyName("AllowedHosts")]

            public string AllowedHosts { get; set; }

            

            public  appsettings LoadFromConfig(IConfiguration config)

            { 

                

                    return config.Get<appsettings>();

                

            }

        }

    

    }

Example Code: https://github.com/ignatandrei/RSCG_Examples/tree/main/appSettingsEditor

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.