Category: .NET Core

RSCG Example – Tiny Types – Part 23

 

 

name BaseTypes
nuget

https://www.nuget.org/packages/AndreasDorfer.BaseTypes/

link https://github.com/Andreas-Dorfer/base-types
author Andreas Dorfer

Generated tiny types from any value type
 

The code that you start with is


    [Int] public partial record DepartmentId;

    public Employee GetFromId(int idDepartment, int idEmployee)

    {

        

        return new Employee()

        {

            ID = idEmployee,

            DepartmentId = idDepartment,

            Name = "Andrei " + idEmployee

    

        };

    }

    public Employee GetFromId(DepartmentId departmentId,  EmployeeId employeeId)

    {

        return GetFromId(departmentId, employeeId);

    }


The code that you will use is



    e.GetFromId(10, 34);

    e.GetFromId(new DepartmentId(34), new EmployeeId(10));

 

The code that is generated is


    [System.ComponentModel.TypeConverter(typeof(AD.BaseTypes.Converters.BaseTypeTypeConverter<DepartmentId, int>))]

    [System.Text.Json.Serialization.JsonConverter(typeof(AD.BaseTypes.Json.BaseTypeJsonConverter<DepartmentId, int>))]

    sealed partial record DepartmentId : System.IComparable<DepartmentId>, System.IComparable, AD.BaseTypes.IBaseType<int>

    {

        public DepartmentId(int value)

        {

            this.Value = value;

        }

        public int Value { get; }

        public override string ToString() => Value.ToString();

        public int CompareTo(object? obj) => CompareTo(obj as DepartmentId);

        public int CompareTo(DepartmentId? other) => other is null ? 1 : System.Collections.Generic.Comparer<int>.Default.Compare(Value, other.Value);

        public static implicit operator int(DepartmentId item) => item.Value;

        public static DepartmentId Create(int value) => new(value);

    }

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

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
Roslyn Source Code Generators

AMA.NET 2

In fiecare joi, la ora 21, o sa tin o sesiune de AMANET – intrebati-ma orice despre .NET .  De preferinta, .NET Core – dar merge si .NET Framework, arhitectura, backend, Razor, MVC, orice care are legatura cu C# . Linkul  pentru video este  https://meet.google.com/idm-gcxj-enb  si cel de la google https://calendar.google.com/event?action=TEMPLATE&tmeid=MTdhZTVnOWZvdTk3ZTJzMjVzMW41bnI5c2FfMjAyMTA4MDVUMTgwMDAwWiBpZ25hdC5hbmRyZWlAbQ&tmsrc=ignat.andrei%40gmail.com&scp=ALL .

Va astept !

AMA.NET

In fiecare joi, la ora 21, o sa tin o sesiune de AMANET – intrebati-ma orice despre .NET .  De preferinta, .NET Core – dar merge si .NET Framework, arhitectura, backend, Razor, MVC, orice care are legatura cu C# . Linkul  pentru video este  https://meet.google.com/idm-gcxj-enb  si cel de la google https://calendar.google.com/event?action=TEMPLATE&tmeid=MTdhZTVnOWZvdTk3ZTJzMjVzMW41bnI5c2FfMjAyMTA4MDVUMTgwMDAwWiBpZ25hdC5hbmRyZWlAbQ&tmsrc=ignat.andrei%40gmail.com&scp=ALL .

Va astept !

RSCG-Integrating with CI Only – part 10

The integration with CI providers is enough straightforward – just see what environments variable those providers have. GitLab and Github has enough documentation – at https://docs.gitlab.com/ee/ci/variables/ and https://docs.github.com/en/actions/reference/environment-variables 

What I have forgot is that there are also CI providers without having necessary source control.

For example AzureDevOps can execute code taken from GitHub

More than that  Heroku stack , that has not source control , have dispersed info – see https://devcenter.heroku.com/changelog-items/630 about SOURCE_VERSION – and no more else about the what other variables it has. I have obtained those via RSCG – but no indication about what is the source control comes.

What can I do ? The solution is to avert the users – I have created https://ignatandrei.github.io/RSCG_AMS/runtimeMessages/NotFound.md .

RSCG–Template Rendering- part 17

 

name Transplator
nuget https://www.nuget.org/packages/Transplator/
link https://github.com/atifaziz/Transplator/
author Atif Aziz

The Transplator is a small fast rendering engine to allow you to make rendering from any class instance.   The code that you start with is


    {%

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

    using Rendering;

    

    static partial class EmployeeRendering

    {

        public static string Render(params Employee[] employees)

        {

            var sb = new StringBuilder();

            int i= 0;

    -%}

    Number Employees: {% employees?.Length %}

        {%~ foreach (var emp in employees) { 

        i++;

        ~%}

        {% i %}. {% emp.Name %}  it is in {% emp.Department?.Name %}

        {%~ } ~%}

    {%

            return sb.ToString();

    

            void WriteText(string value) =&gt; sb.Append(value);

            void WriteValue(object value) =&gt; sb.Append(value);

        }

    }

    ~%}


The code that you will use is



    var IT = new Department();

    IT.Name = "IT";

    var e = new Employee();

    e.ID = 10;

    e.Name = "Andrei Ignat";

    e.Department = IT;

    var render = EmployeeRendering.Render(e);

    Console.WriteLine(render);

The code that is generated is


    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

    using Rendering;

    

    static partial class EmployeeRendering

    {

        public static string Render(params Employee[] employees)

        {

            var sb = new StringBuilder();

            int i= 0;

    WriteText(@"Number Employees: ");

    WriteValue(employees?.Length);

    WriteText(@"

    ");

    foreach (var emp in employees) { 

        i++;

       WriteText(@"    ");

    WriteValue(i);

    WriteText(@". ");

    WriteValue(emp.Name);

    WriteText(@"  it is in ");

    WriteValue(emp.Department?.Name);

    WriteText(@"

    ");

    }

            return sb.ToString();

    

            void WriteText(string value) =&gt; sb.Append(value);

            void WriteValue(object value) =&gt; sb.Append(value);

        }

    }

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

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
Roslyn Source Code Generators

RSCG–Advice- part 8

If you create a Roslyn Source Code Generator that uses inside a base class or a static class, the best way, in my opinion , is to create 2 separate nuget packages : one for the code generated and one for the base class. This way the project that will use your RSCG will use just the nuget package that have the base class and get rid of the generator . More, you can add a third project to register some extensions to Web project .

Let’s see in practice with RSCG_AMS :

I have a NuGet package with the base class AboutMySoftware –  https://www.nuget.org/packages/AMS_Base/

The RSCG generates derived classes of AboutMySoftware  – has a dependency in the AMS_Base   –  the name is RSCG_AMS : https://www.nuget.org/packages/RSCG_AMS/ . This will generate code during the build and will not be included in the output.

Finally, I have an extension for WebAPI – AMSWebAPI – that registers the  endpoint in the Web Project to display /ams url .

I think that this is a normal organization of RSCG projects.

 

Also, add documentation  for each class / public item generated. There can be projects that will require this ( as a compiler/ property flag )

RSCG–AMS – About My software –Documentation– part 7

Now it is time to let others know about the project. And the first step is to make documentation. And , because a picture is worth many words, here is the picture:

Also, instructions about how to use will help the programmers:

For a DLL it is simple :

<ItemGroup>
    <PackageReference Include="AMS_Base" Version="2021.6.29.1820" />
    <PackageReference Include="RSCG_AMS" Version="2021.6.29.1820" ReferenceOutputAssembly="false" OutputItemType="Analyzer" />
  </ItemGroup>

For an ASP.NET Core application:

  <PackageReference Include="AMSWebAPI" Version="2021.6.29.1820" />
    <PackageReference Include="AMS_Base" Version="2021.6.29.1820" />
    <PackageReference Include="RSCG_AMS" Version="2021.6.29.1820" ReferenceOutputAssembly="false" OutputItemType="Analyzer" />

and the code will be

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
    endpoints.UseAMS();
});

RSCG–AMS – About My software –Reading csproj– part 6

Now it is time to put some more data – like authors and version. I have read a lot ( and tried a lot) about  CompilerVisibleProperty and  CompilerVisibleItemMetadata ( see https://github.com/dotnet/roslyn/blob/main/docs/features/source-generators.cookbook.md  ) . However, I was unable to get the data ( Authors and Version) from there .

So this is what I was get, to read the csproj near the program:

private ItemsFromCSPROJ TryGetPropertiesFromCSPROJ(GeneratorExecutionContext context)
{
    var ret= new ItemsFromCSPROJ();
    try
    {
        var dirFolder = ((dynamic)(context.Compilation)).Options?.SourceReferenceResolver?.BaseDirectory;
        if (string.IsNullOrWhiteSpace(dirFolder))
            return ret;

        var file = Directory.GetFiles(dirFolder, "*.csproj");
        if (file.Length != 1)
            throw new ArgumentException($"find files at {dirFolder} :{file.Length} ");

        var xmldoc = new XmlDocument();
        xmldoc.Load(file[0]);
        XmlNode node;
        node = xmldoc.SelectSingleNode("//Authors");
        ret.Authors = node?.InnerText;
        node = xmldoc.SelectSingleNode("//Version");
        ret.Version = node?.InnerText;
        return ret;
    }
    catch(Exception )
    {
        //maybe log warning? 
        return ret;
    }

}

Next time I will show how it looks

RSCG–AMS – About My software –NuGet– part 5

The problem with RSCG is to differentiate  between the generator and the code generated. In my case , the base class should be in one nuget, the generator in other ( to can remove it from build) and the WebAPI in another.

That took me a whole day and the result is ok . Pain Points:

https://turnerj.com/blog/the-pain-points-of-csharp-source-generators 

CI action and Deploy to nuget

PackageReference Include=”Microsoft.VisualStudio.Web.CodeGeneration.Design

Now it works for WebAPI with

<PackageReference Include=”AMSWebAPI” Version=”2021.6.26.1937″ />
<PackageReference Include=”AMS_Base” Version=”2021.6.26.1937″ />
<PackageReference Include=”RSCG_AMS” Version=”2021.6.26.1937″ ReferenceOutputAssembly=”false” OutputItemType=”Analyzer” />

And I hve seen that I am not the only one to differentiate between CI servers – for example,

https://github.com/VerifyTests/DiffEngine/blob/master/src/DiffEngine/BuildServerDetector.cs

https://github.com/dotnet/Nerdbank.GitVersioning/blob/master/src/NerdBank.GitVersioning/CloudBuildServices/GitLab.cs

https://github.com/cake-build/cake/blob/develop/src/Cake.Common.Tests/Fixtures/Build/GitLabCIInfoFixture.cs

But now the work is done and you can access all AMS via web ,

app.UseEndpoints(endpoints =>
             {
                 endpoints.MapControllers();
                 endpoints.UseAMS();
             });

either to AMS/index.html , either to AMS/all .

RSCG–AMS – About My software –WebAPI– part 4

Now it should be an easy way to see in the WebAPI. First, return the data for all software that respected that :

public static IEndpointRouteBuilder UseAMS(this IEndpointRouteBuilder endpoints)
{
    endpoints.MapGet("/ams/All", async app =>
    {
                
            var data = AboutMySoftware.AllDefinitions.Select(it => it).ToArray();
        await app.Response.WriteAsJsonAsync(data);
    });
    return endpoints;
}

Now, how can I make a small html to display things ? I can do with Razor Library – but it is too big and maybe the developers do not want to have this dependency. So I decided for https://www.nuget.org/packages/Transplator/  – fairly easy to use. And is another RSCG that converts template code into C#  code.

So now the code looks like this:

public static IEndpointRouteBuilder UseAMS(this IEndpointRouteBuilder endpoints)
{
    endpoints.MapGet("/ams/All", async app =>
    {
                
            var data = AboutMySoftware.AllDefinitions.Select(it => it).ToArray();
        await app.Response.WriteAsJsonAsync(data);
    });
    endpoints.MapGet("/ams/index", app =>
    {
        var response = new ASMTemplate().Render();
        app.Response.ContentType = "text/html";
        return app.Response.WriteAsync(response);
    });
    return endpoints;
}

where the ASMTemplate is

<style>
table {
  font-family: arial, sans-serif;
  border-collapse: collapse;
  width: 100%;
}

td {
  border: 1px solid #dddddd;
  text-align: left;
  padding: 8px;
}

th{
background-color: black;
  color: white;
  border: 1px solid #dddddd;
  text-align: left;
  padding: 8px;
  }
tr:nth-child(even) {
  background-color: #dddddd;
}
</style>

<table>
<tr>
<th>Nr</td>
<th>Component</th>
<th>Date</th>
<th>Commit</th>
<th>RepoUrl</th>
</tr>

{%~ int i=1; ~%}
{%~ foreach(var item in AMS.AboutMySoftware.AllDefinitions){ %}
<tr>
<td>{% i++ %}</td>
<td>{% item.Key %} </td>
<td>{% item.Value.DateGenerated %} </td>
<td>{% item.Value.CommitId %} </td>
<td>{% item.Value.RepoUrl %}</td>
</tr>
{% } %}
</table>

It is time now to make the nuget packages.

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.