Category: roslyn

TILT-Server Timing in Browser-part 19

For see fast what is happening with your server, you can use the Server Timing API.

One of the implementations for .NET is https://github.com/tpeczek/Lib.AspNetCore.ServerTiming .

Not a big deal – implemented separately with AOPMethodsCommon – but I think it is a good idea to use it.

The implementation was straightforward

builder.Services.AddScoped<ServerTiming>();
var app = builder.Build();
app.UseServerTiming();

And then use DI to get the instance of the class.

With this occasion , look how is different calling same method twice

vs

If you do not realize, there are 50 ms vs 1 ms . That difference is from caching

private async Task<TILT_Note_Table[]?> privateLatestTILTs(string urlPart, int numberTILTS)
{
    if (cache.TryGetValue<TILT_Note_Table[]>(urlPart, out var result))
    {
        return result;
    }
//code
}

Nice, right ? See the timing right in your browser.

Tools used

https://github.com/tpeczek/Lib.AspNetCore.ServerTiming

https://github.com/ignatandrei/AOP_With_Roslyn/

DIForFunctions–improving generating of a constructor for a existing class with Roslyn

It is not so difficult to generate a new constructor for a existing class with Roslyn – however , there are some difficulties:

1. The original class must be declared “partial” – you cannot declare the second as partial if the first was not

2.  The new class must be in the same namespace ( or without namespace) as the original

3. The constructor must have the types declared with  full names  / or get the usings from the existing class ( or , maybe , put the developer to use global usings: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10

For 1 the dotnet compiler shows an error  – but you can add yours too:

private static bool HasPartial(ClassDeclarationSyntax cds)
         {
             if (cds.Modifiers.Count < 1)
                 return false;
             foreach(var modif in cds.Modifiers)
             {
                 if (modif.Text == “partial”)
                     return true;
             }
             return false;
         }

if (!HasPartial(cds))
                 {
                     var dd = new DiagnosticDescriptor(DiagnosticId, Title, “Please add partial to ” + cds.Identifier.Text, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: “Please add partial to ” + cds.Identifier.Text);
                     Location? loc = cds.GetLocation();
                     var dg = Diagnostic.Create(dd, loc);
                     context.ReportDiagnostic(dg);
                     return;
                 }

For 2 is somehow more difficult

private static Tuple<string,string> NameAndNameSpace(ClassDeclarationSyntax c)
         {
             var nameClass = c.Identifier.Text;
             var p = c.Parent;
             var namespaceClass = “”;
             while (true)
             {
                 if (p is BaseNamespaceDeclarationSyntax bnsds)
                 {
                     namespaceClass = bnsds.Name.ToFullString();
                     break;
                 }
                 p = p?.Parent;
                 if (p == null)
                     break;
             }
             return Tuple.Create(nameClass, namespaceClass);
         }

And verify if the namespace is null – if it is, do NOT generate namespace declaration

For 3 it is more complicated

private static string? FullTypeVar(SemanticModel? sem,TypeSyntax? ts)
         {
             if (sem == null)
                 return null;
             if (ts is null)
                 return null;
             var typeInfo = sem.GetTypeInfo(ts);
             if (typeInfo.Type is null)
                 return null;

            var theType = ((INamedTypeSymbol)typeInfo.Type);

            var typeField = theType?.ToDisplayString();
             return typeField;
         }

And that it is!

DIForFunctions – Improving constructor–part 5

I have received a suggestion : what if we just put into constructor what we need , and everything else ( such as ILogger ) are into fields ?

The Roslyn Source Code Generator will generate a constructor that calls the this constructor  and will assign fields needed.

Let’s give an example : We wrote

public partial class TestDIFunctionAdvWithConstructor2Args
    {
        [RSCG_FunctionsWithDI_Base.FromServices]
        private TestDI1 NewTestDI1;

       public TestDI2 NewTestDI2 { get; set; }

       public readonly TestDI3 myTestDI3;

       private TestDIFunctionAdvWithConstructor2Args(TestDI3 test, TestDI2 a)
        {
            myTestDI3 = test;
            NewTestDI2 = a;
        }

   }

and the generator will generate a new constructor with the required  field

public partial class TestDIFunctionAdvWithConstructor2Args
{
public TestDIFunctionAdvWithConstructor2Args  
(TestDI3 test, TestDI2 a, TestDI1 _NewTestDI1) : this (test,a)
{
this.NewTestDI1 = _NewTestDI1;
}//end constructor

}//class

The code is non trivial  – to find if a constructor exists, take his fields, generate new constructor with all fields.

But , as anything in IT , it is doable .

DIForFunctions–what it does- part 4

You can find a demo at https://github.com/ignatandrei/FunctionsDI/tree/main/src/FunctionsWithDI  – see TestCOnsoleAPP. But let’s write here also

Generate (constructor) and functions calls similar with ASP.NET Core WebAPI ( [FromServices] will be provided by DI ) Also, verifies for null .

Usage

Reference into the csproj

<ItemGroup>
    <PackageReference Include="RSCG_FunctionsWithDI" Version="2022.6.19.1605" ReferenceOutputAssembly="false" OutputItemType="Analyzer" />
    <PackageReference Include="RSCG_FunctionsWithDI_Base" Version="2022.6.19.1605" />
</ItemGroup>	

Then for every class you can write [FromServices]

using RSCG_FunctionsWithDI_Base;
//namespace if necessary
public partial class TestDIFunction
{
    public bool TestMyFunc1([FromServices] TestDI1 t1, [FromServices] TestDI2 t2, int x, int y)
    {
        return true;
    }
    //more functions
}

generates the constructor with needed details

public partial class TestDIFunction
{ 
private TestDI1 _TestDI1;
private TestDI2 _TestDI2;
public TestDIFunction  (TestDI1 _TestDI1,TestDI2 _TestDI2) //constructor generated with needed DI
 { 
this._TestDI1=_TestDI1;
this._TestDI2=_TestDI2;

 } //end constructor 

//making call to TestMyFunc1
public bool TestMyFunc1(int  x,int  y){ 
var t1 = this._TestDI1  ;
if(t1 == null) throw new ArgumentException(" service TestDI1  is null in TestDIFunction ");
var t2 = this._TestDI2  ;
if(t2 == null) throw new ArgumentException(" service TestDI2  is null in TestDIFunction ");
return  TestMyFunc1(t1,t2,x,y);
}

so you can call

var test=serviceProvider.GetService<TestDIFunction>();
Console.WriteLine(test.TestMyFunc1(10,3)); // calling without the [FromServices] arguments

DI for Functions- work–part 2

Let’s begin with tests  – we need to have a class with multiple functions that have multiple [FromServices} parameter. Like

public bool TestMyFunc1([FromServices] TestDI1 t1, [FromServices] TestDI2 t2, int x, int y)
         {
             return true;
         }
         public bool TestMyFunc2([FromServices] TestDI1 t12,  int x, int y)
         {
             return true;
         }

// more others

Because there are multiple functions, I need to generate very fast  – so Incremental generators to the rescue . They are documented here : https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md  . And a good tutorial is to be found at https://andrewlock.net/creating-a-source-generator-part-1-creating-an-incremental-source-generator/ .

Basically, this is the code

IncrementalValuesProvider<MethodDeclarationSyntax> paramDeclarations = context.SyntaxProvider
             .CreateSyntaxProvider(
                 predicate: static (s, _) => IsSyntaxTargetForGeneration(s),
                 transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx))
             .Where(static m => m is not null)!; // filter out attributed enums that we don’t care about

            IncrementalValueProvider<(Compilation, ImmutableArray<MethodDeclarationSyntax>)> compilationAndEnums = context.CompilationProvider.Combine(paramDeclarations.Collect());

            context.RegisterSourceOutput(compilationAndEnums,
             static

and the  idea is to find the parameters of the function that has attributes – and one of those is [FromServices] . After that , find the methods that are the parent – and then the class. After that , is simple to generate a constructor with all (distinct) the [FromServices]parameters and construct the similar method with just the non-DI parameters.

Bonus : We can verify if the parameters are null and throw exception

I could do a template for defining , but – wait to see if gain some traction to modify .

You can find the sources at https://github.com/ignatandrei/functionsdi and the NuGet packages ( one with generator, one with [FromServices] ) at https://www.nuget.org/packages/RSCG_FunctionsWithDI and https://www.nuget.org/packages/RSCG_FunctionsWithDI_Base

Enjoy!

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

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.