Category: roslyn

RSGC-JSON to Class- part 4

 

 

name JsonByExampleGenerator
nuget

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

link https://github.com/hermanussen/JsonByExampleGenerator/
author Robin Hermanussen

This will generate C# classes from json files.
 

The code that you start with is


    {

    "FirstName": "Andrei",

    "LastName": "Ignat",

    "Blog": "http://msprogrammer.serviciipeweb.ro/"


The code that you will use is



    var p1 = new Person();

    p1.Blog = "http://msprogrammer.serviciipeweb.ro/";

    var config = new ConfigurationBuilder()

      .AddJsonFile("persons.json")

      .Build();

    

    var p = config.Get<Person>();

    var p2 = Person.FromConfig(config);

 

The code that is generated is


    [DataContract(Name = "Person", Namespace = "JsonToClass.Json.Persons")]

    public partial class Person

    {

    [DataMember(Name = "FirstName", EmitDefaultValue = false, Order = 0)]

    public string FirstName { get; set; }

    [DataMember(Name = "LastName", EmitDefaultValue = false, Order = 1)]

    public string LastName { get; set; }

    [DataMember(Name = "Blog", EmitDefaultValue = false, Order = 2)]

    public string Blog { get; set; }

    

    public static Person FromConfig([System.Diagnostics.CodeAnalysis.NotNull] IConfiguration config)

    {

    return config.Get<Person>();

    }

    }

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

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

RSGC-Enum-part 3

 

 

name Enum
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 generate code to fast parsing a int or a string to an enum
 

The code that you start with is


    [AutoEnum(template = EnumMethod.GenerateExtensionCode)]

    public enum MathematicalOperation

    {

    None=0,

    Add=1,

    Multiplication=2

    }


The code that you will use is



    var fromInt = enumMathematicalOperation.ParseExactMathematicalOperation(1);

    var fromString = enumMathematicalOperation.ParseExactMathematicalOperation("add");

    Console.WriteLine(fromInt + "-"+fromString);

 

The code that is generated is


      [GeneratedCode("AOPMethods", "")] 

      [CompilerGenerated]

      public  static partial class enumMathematicalOperation{ 

       /*

        public static int idMathematicalOperation(){

        System.Diagnostics.Debugger.Break();

        return 1;

        }

        */

        public static RSCG_Enum.MathematicalOperation ParseExactMathematicalOperation(this long value, RSCG_Enum.MathematicalOperation? defaultValue = null){

                if(0 == value)

                    return RSCG_Enum.MathematicalOperation.None;

                        if(1 == value)

                    return RSCG_Enum.MathematicalOperation.Add;

                        if(2 == value)

                    return RSCG_Enum.MathematicalOperation.Multiplication;

            

            if(defaultValue != null)

                return defaultValue.Value;

    

            throw new ArgumentException("cannot find " + value +" for RSCG_Enum.MathematicalOperation  ");

        }

       

        public static RSCG_Enum.MathematicalOperation ParseExactMathematicalOperation(this string value, RSCG_Enum.MathematicalOperation? defaultValue = null){

            //trying to see if it is a value inside

            //if(!string.IsNullOrWhiteSpace)

            if(long.TryParse(value, out long valueParsed)){

                return ParseExactMathematicalOperation(valueParsed);

            }

    

                if(0==string.Compare("None" , value, StringComparison.InvariantCultureIgnoreCase))

                    return RSCG_Enum.MathematicalOperation.None;

                        if(0==string.Compare("Add" , value, StringComparison.InvariantCultureIgnoreCase))

                    return RSCG_Enum.MathematicalOperation.Add;

                        if(0==string.Compare("Multiplication" , value, StringComparison.InvariantCultureIgnoreCase))

                    return RSCG_Enum.MathematicalOperation.Multiplication;

            

    

            if(defaultValue != null)

                return defaultValue.Value

            throw new ArgumentException("cannot find " + value +" for RSCG_Enum.MathematicalOperation  ");

        }

        /*

        

        */

        

      }

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

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- AppVersion–part 2

 

 

name ThisAssembly
nuget https://www.nuget.org/packages/ThisAssembly
link https://www.clarius.org/ThisAssembly/
author Daniel Cazzulino

The ThisAssembly.Info allows you access to the Assembly Information as constants, instead of going to reflection each time. I found useful to see the assembly version right away in any project that I have.

 

The code that you start with is in .csproj

<PropertyGroup>
<Version>2021.2.15.800</Version>
</PropertyGroup>

The code that you will use is

var strVersion = ThisAssembly.Info.Version;
Console.WriteLine(strVersion);

 

The code that is generated is

/// <summary>
/// Provides access to the current assembly information as pure constants, 
///  without requiring reflection.
/// </summary>
partial class ThisAssembly
{
    /// <summary>
    /// Gets the AssemblyInfo attributes.
    /// </summary>
    [GeneratedCode("ThisAssembly.AssemblyInfo", "1.0.0")]
    [CompilerGenerated]
    public static partial class Info
    {
        public const string Company = @"RSCG_Version";

        public const string Configuration = @"Debug";

        public const string FileVersion = @"2021.2.15.800";

        public const string InformationalVersion = @"2021.2.15.800";

        public const string Product = @"RSCG_Version";

        public const string Title = @"RSCG_Version";

        public const string Version = @"2021.2.15.800";

    }
}

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

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–part 1

Roslyn Source Code Generators are a easy way to generate automatically code that can be injected at compile time. This code can be generated on some template file or based on existing code or both . It works by intercepting at compile time the result of compilation of the existing source code and adding to this compilation other files. It cannot modify the code, just add to it.

You can see a deep tutorial about how to do it at https://khalidabuhakmeh.com/dotnet-5-source-generators-jump-start . Also, you can read more at https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/ and at https://github.com/dotnet/roslyn/blob/master/docs/features/source-generators.cookbook.md .

There are a fair amount of them  – and I intend to detail their use .  That means

1. I will show how they can be useful for you

2. I will show examples with source code .

3. Make a video with this.

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

AOPMethods–dogfooding

I was trying to apply AOPMethods to – surprise! –  AOPMethods project itself. And I have discovered a new reason: I do not want to make the methods public. I just want to put try/catch around them to know what is wrong.

The fast – and not so good – idea was to transform

MethodPrefix =”pub”

into a

Dictionary<MethodsPrefix: string,  VIsibilty: string>

in order to pass something like that

{ “pub”, “public” } , {“prv”, “private”}

The second idea was better : what if I allow multiple instances and generate accordingly  ?

So I came up with this definition

[AutoMethods(template = TemplateMethod.MethodWithPartial, MethodPrefix =”pub”)]
[AutoMethods(template = TemplateMethod.CustomTemplateFile,CustomTemplateFileName =”privateTryCatch.txt”,  MethodSuffix = “bup”)]
partial class Person

And the code  processes both attributes – and generates the code. One slightly problem: Cannot have GeneratedCode and CompilerGenerated on both generated files. But – it works!

AOP Methods–Problems in running and solving

The problems that I have encountered were:

1.  The ThisAssembly  RoslynGenerator that I use should not be put as reference in the nuget. I have fixed this by adding

<PackageReference Include=”ThisAssembly.AssemblyInfo” Version=”1.0.0″ ReferenceOutputAssembly=”false” />

2. I have problems generating code to async code with Task . The problem was that I have added logging ( very basic – Console.WriteLine) and the order of the logging was errating. I figured out that I should must use the async /await and then my template was modified as

strAwait = “”
strAsync =””
if mi.IsAsync == true
     strAwait = ” await “
     strAsync  = ” async “
end

public {{strAsync}} {{mi.ReturnType}} {{mi.NewName}} ({{mi.parametersDefinitionCSharp }} {{separator}} 

//more code

{{
     if mi.ReturnsVoid == false
}}
     return
{{
     end
}}
{{  strAwait }}

{{mi.Name}}({{ mi.parametersCallCSharp }});

And this is worth noting.

AOP Methods–Code

The code is not so much different from SkinnyControllers : Implement ISourceGenerator , putting the Generator attribute on the class

[Generator]
    public partial class AutoActionsGenerator : ISourceGenerator

inspecting the classes if they have the common attribute , generating code with Scriban

The problem was : How can the AOPMethods can

  1. differentiate between the private function that must be made public
  2. generate the code for a similar public function, with same parameters (and maybe more) , but with different name ?

So I decide to go the route of convention: The programmer will declare the private function that he wants to autmatically make public ( and add templating code ) with a prefix or a suffix . This will be declared into the attribute of the class – and that will be all.

For example , I have this method

public  string FullName()
{
            
     return FirstName + ” ” + LastName;
}

That I want to monitor ( add logs, info and so on ) . I will make it private and start with pub

private string pubFullName()
{
            
     return FirstName + ” ” + LastName;
}

Then , on the class , I will declare the pub prefix

[AutoMethods(template = TemplateMethod.CallerAtttributes, MethodPrefix =”pub”, MethodSuffix =”bup”)]

And the following will be generated

public string FullName(
[CallerMemberName] string memberName = “”,
[CallerFilePath] string sourceFilePath = “”,
[CallerLineNumber] int sourceLineNumber = 0)
{
     try
     {
         Console.WriteLine(“–pubFullName start “);
         Console.WriteLine(“called class :” + memberName);
         Console.WriteLine(“called file :” + sourceFilePath);
         Console.WriteLine(“called line :” + sourceLineNumber);

        return

    pubFullName();
     }
     catch (Exception ex)
     {
         Console.WriteLine(“error in pubFullName:” + ex.Message);
         throw;
     }
     finally
     {
         Console.WriteLine(“——–pubFullName end”);
     }

}

The caller attributes templates can be found at  AOP_With_Roslyn\AOPMethods\AOPMethods\templates\CallerAtttributes.txt

//——————————————————————————
// <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.CodeDom.Compiler;
using System.Runtime.CompilerServices;

namespace {{NamespaceName}} {

    /// <summary>
     /// Generates code from  {{ClassName}}
     /// </summary>
   [GeneratedCode(“AOPMethods”, “{{version}}”)]
   [CompilerGenerated]
   partial class {{ClassName}}{
    /*
     public int id(){
     System.Diagnostics.Debugger.Break();
     return 1;
     }
     */
     {{~ for mi in Methods ~}}
         {{
             separator = “”
             if(mi.NrParameters > 0)
                 separator = “,”
             end
             strAwait = “”
             strAsync =””
             if mi.IsAsync == true
                 strAwait = ” await “
                 strAsync  = ” async “
             end
         }}
         public {{strAsync}} {{mi.ReturnType}} {{mi.NewName}} ({{mi.parametersDefinitionCSharp }} {{separator}} 
         [CallerMemberName] string memberName = “”,
         [CallerFilePath] string sourceFilePath = “”,
         [CallerLineNumber] int sourceLineNumber = 0){
             try{
                 Console.WriteLine(“–{{mi.Name}} start “);
                 Console.WriteLine(“called class :”+memberName );
                 Console.WriteLine(“called file :”+sourceFilePath );
                 Console.WriteLine(“called line :”+sourceLineNumber );
             {{
                 if mi.ReturnsVoid == false
             }}
                 return
             {{
                 end
             }}
             {{  strAwait }}

            {{mi.Name}}({{ mi.parametersCallCSharp }});
             }
             catch(Exception ex){
                 Console.WriteLine(“error in {{mi.Name}}:” + ex.Message);
                 throw;
             }
             finally{
                 Console.WriteLine(“——–{{mi.Name}} end”);
             }

         }

     
     {{~ end ~}}   
    
   }
}             

You can add logging , security, anything else that is a vertical to the business

AOP Methods–Introduction

As I have done with Roslyn for SkinnyControllers , I said – what about generating public methods at compile time ?

For example, what if this method

private string pubFullName()
{

return FirstName + ” ” + LastName;

}

 

is transformed into this

public string FullName(
[CallerMemberName] string memberName = “”,
[CallerFilePath] string sourceFilePath = “”,
[CallerLineNumber] int sourceLineNumber = 0)
{
try
{
Console.WriteLine(“–pubFullName start ” + _cc);
Console.WriteLine(“called class :” + memberName);
Console.WriteLine(“called file :” + sourceFilePath);
Console.WriteLine(“called line :” + sourceLineNumber);

return pubFullName();
}
catch (Exception ex)
{
Console.WriteLine(“error in pubFullName:” + ex.Message);
throw;
}
finally
{
Console.WriteLine(“——–pubFullName end”);

}
}

automatically, based on a template ? And all methods will have this ?

Enter AOP Methods : https://www.nuget.org/packages/AOPMethodsGenerator/  and https://www.nuget.org/packages/AOPMethodsCommon/

The first one is the generator. The second one is containing the attribute that tells to transform.

AutoActions for Skinny controllers–custom template

Now I want to let the user make his own template. For this, I have enriched the attribute AutoActionsAttribute with a

public string CustomTemplateFileName { get; set; }

 

The code was pretty easy, just reading from GeneratorExecutionContext . AdditionalFiles instead of reading from the template in the dll

 

switch (templateId)
{

case TemplateIndicator.None:
	context.ReportDiagnostic(DoDiagnostic(DiagnosticSeverity.Info, $"class {myController.Name} has no template "));
	continue;
case TemplateIndicator.CustomTemplateFile:

	var file = context.AdditionalFiles.FirstOrDefault(it => it.Path.EndsWith(templateCustom));
	if (file == null)
	{
		context.ReportDiagnostic(DoDiagnostic(DiagnosticSeverity.Error, $"cannot find {templateCustom} for  {myController.Name} . Did you put in AdditionalFiles in csproj ?"));
		continue;
	}
	post = file.GetText().ToString();
	break;

default:
	using (var stream = executing.GetManifestResourceStream($"SkinnyControllersGenerator.templates.{templateId}.txt"))
	{
		using var reader = new StreamReader(stream);
		post = reader.ReadToEnd();

	}
	break;
}

 

There are 2 small catches

1 see the EndsWith  ? The GeneratorExecutionContext . AdditionalFiles  gives you the full path

2. the additional files should be registered in the .csproj

<ItemGroup>
<AdditionalFiles Include=”Controllers\CustomTemplate1.txt” />
</ItemGroup>

 

Now the user can define his own template for the controller like this


[AutoActions(template = TemplateIndicator.CustomTemplateFile, FieldsName = new[] { "*" } ,CustomTemplateFileName = "Controllers\\CustomTemplate1.txt")]
    [Route("api/[controller]/[action]")]
    [ApiController]
    public partial class CustomTemplateController : ControllerBase
    {
        private readonly RepositoryWF repository;

        public CustomTemplateController ()
        {
            //do via DI
            repository = new RepositoryWF();
        }

    }

And this is all ! ( ok. some documentation should be involved)

AutoActions for Skinny controllers–code improvements and more docs

I realized that this code

Assembly.GetExecutingAssembly();

was executing for each controller. So I decided to move to a class variable and attribute once.

Also, I may want to have all fields – so I decided to express via a special field *

The code modifications were, thanks to Linq, pretty small:

bool All = fields.Contains(“*”);

var memberFields = myController
     .GetMembers()
     .Where(it => All || fields.Contains(it.Name))

Also,  modified the documents to show most errors and their problems:

error CS0260: Missing partial modifier on declaration of

Failed to load API definition.
Fetch error undefined /swagger/v1/swagger.json

Any open source project becomes a marathon, not a sprint.

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.