RSCG–AMS – About My software –idea – part 1

Every product should have an About page . In the About page should be listed

  1. The product name
  2. The version  of the product
  3. Link to latest version ?
  4. Built date+ time
  5. The commit ID
  6. The authors
  7. Link to the License
  8. Other components version and link to about
  9. Third Party notices
  10. Repository  link ( github, gitlab, …)
  11. Documentation Link
  12. Release Notes link
  13.   Maybe log file ?
  14. Maybe latest errors ?
  15. Maybe system.info ?

 

This should be available for

  1. any dll – as a class
  2. any console project – as Console.WriteLine
  3. for any ASP.NET Core app
    1.   as a class
    2. as a  WebAPI
    3. as an HTML UI

You can see an example at https://netcoreblockly.herokuapp.com/AMS

Benchmarking RSCG vs Reflection

I make a microservices Buffet . In this I consider having email as a service . When the DevOps wants email, he can choose between various plugins ( simple smtp email, gmail, exchange, others). Those plugins can have various properties – that must be edited by the primary administrator of the microservice. The properties can be discovered at runtime ( via Reflection ) o r at build time ( via Roslyn Source Code Generators  – RSCG ).

But – we should see what is faster, right ?   And the feeling is that RSCG is always faster – but it is , really ? Let’s see…

So = let’s make a test with https://github.com/dotnet/BenchmarkDotNet . You can have the source code by going to https://github.com/ignatandrei/AOP_With_Roslyn/tree/master/AOPMethods .

First , the class that is tested

public partial class EmailSmtpClientMS 
{

    public EmailSmtpClientMS()
    {

        Port = 25;

    }
    public string Name { get; set; }


    public string Type
    {
        get
        {
            return this.GetType().Name;
        }
    }
    public string Host { get; set; }
    public int Port { get; set; }

    public string Description
    {
        get
        {
            return $"{Type} {Host}:{Port}";
        }
    }
}

 

Second, with AOPMethods I generate the read properties values – properties that  you can read – via a dictionary and via a switch. This can be achieved simply :

[AutoMethods(template = TemplateMethod.CustomTemplateFile, CustomTemplateFileName = "ClassToDictionary.txt")]
    public partial class EmailSmtpClientMS 

And this will be generated by RSCG for the switch

protected object GetValueProperty(string propName)
{
    switch (propName)
    {
        //true true
        case "Name":
            return this.Name;


        //true false
        case "Type":
            return this.Type;


        //true true
        case "Host":
            return this.Host;


        //true true
        case "Port":
            return this.Port;


        //true false
        case "Description":
            return this.Description;



        default:
            throw new ArgumentException("cannot find property " + propName);
    }
}

and for the dictionary

private IDictionary<string, PropertyHelper> MyProperties()
{
    var data = new Dictionary<string, PropertyHelper>();
    PropertyHelper ph;
    ph = new PropertyHelper();
    ph.Name = "Name";
    ph.Type = "string";
    ph.CanRead = !false;
    ph.CanWrite = !false;

    ph.Value = this.Name;

    data.Add("Name", ph);
    //Name string     
    ph = new PropertyHelper();
    ph.Name = "Type";
    ph.Type = "string";
    ph.CanRead = !false;
    ph.CanWrite = !true;

    ph.Value = this.Type;

    data.Add("Type", ph);
    //Type string     
    ph = new PropertyHelper();
    ph.Name = "Host";
    ph.Type = "string";
    ph.CanRead = !false;
    ph.CanWrite = !false;

    ph.Value = this.Host;

    data.Add("Host", ph);
    //Host string     
    ph = new PropertyHelper();
    ph.Name = "Port";
    ph.Type = "int";
    ph.CanRead = !false;
    ph.CanWrite = !false;

    ph.Value = this.Port;

    data.Add("Port", ph);
    //Port int     
    ph = new PropertyHelper();
    ph.Name = "Description";
    ph.Type = "string";
    ph.CanRead = !false;
    ph.CanWrite = !true;

    ph.Value = this.Description;

    data.Add("Description", ph);
    //Description string     

    return data;

}

 

The spec for benchmark are :


BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19043.1052 (21H1/May2021Update)
Intel Core i7-6600U CPU 2.60GHz (Skylake), 1 CPU, 4 logical and 2 physical cores
.NET SDK=5.0.301
  [Host]     : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT
  DefaultJob : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT

 

Third, I benchmark obtaining one single property – the Host – via the 3 methods:

public partial class EmailSmtpClientMSOneProperty: EmailSmtpClientMS
{
    [Benchmark]
    public string GetHostReflection()
    {
        return this.GetType().GetProperty("Host").GetValue(this).ToString();
    }
    [Benchmark]
    public string GetHostViaDictionary()
    {
        return this.ReadMyProperties()["Host"].ToString();
    }
    [Benchmark]
    public string GetHostViaSwitch()
    {
        return this.GetValueProperty("Host").ToString();
    }
}

And in Program.cs

BenchmarkRunner.Run<EmailSmtpClientMSOneProperty>(
    ManualConfig
        .Create(DefaultConfig.Instance)
        .WithOption(ConfigOptions.DisableOptimizationsValidator, true)
        );

( of course, I have added on the class
//[SimpleJob(RuntimeMoniker.Net50)]
//[ShortRunJob(RuntimeMoniker.Net50)]
//[DryJob(RuntimeMoniker.Net50)]
[Orderer(SummaryOrderPolicy.FastestToSlowest, MethodOrderPolicy.Declared)]
//[SimpleJob(RuntimeMoniker.NetCoreApp31)]
[RPlotExporter]
[CsvMeasurementsExporter]
[MemoryDiagnoser]
[HtmlExporter]
[MarkdownExporterAttribute.GitHub]
)
The results are in ns –so, the less/smaller , that means  better results.

The results are here in HTML form :

 

 

Method Mean Error StdDev Median Gen 0 Gen 1 Gen 2 Allocated
GetHostViaSwitch 18.07 ns 0.434 ns 0.549 ns 18.02 ns
GetHostReflection 144.13 ns 2.582 ns 5.501 ns 142.13 ns
GetHostViaDictionary 451.59 ns 12.363 ns 33.635 ns 441.72 ns 0.3057 640 B

The graphic may be more interesting:

 

Surprised ? The RSCG Switch Property is indeed the fastest one – but the Reflection is faster than RSCG Property Dictionary ( or , at least , for my implementation).

However, I realized that in real life , I will retrieve all properties in a Dictionary to be edited . So all implementations should occur the penalty of creating a Dictionary . Time for next benchmark . This time , the code is

[Benchmark]
public IDictionary<string,object> GetHostReflection()
{
    var props = this.GetType()
        .GetProperties()
        .Where(it=> it.CanWrite)
        .ToDictionary(it => it.Name, it=>it.GetValue(this));
        ;
    return props;
                
}
[Benchmark]
public IDictionary<string, object> GetHostViaDictionary()
{
    var props = this.ReadMyProperties();
    return props;
            
}
[Benchmark]
public IDictionary<string, object> GetHostViaSwitch()
{
    var props = ReadProperties
        .ToDictionary(it => it, it => GetValueProperty(it));
    return props;
            
            
}

 

and the results are:

Method Mean Error StdDev Median Gen 0 Gen 1 Gen 2 Allocated
GetHostViaDictionary 462.1 ns 14.70 ns 40.97 ns 453.6 ns 0.3052 640 B
GetHostViaSwitch 479.5 ns 7.34 ns 7.54 ns 479.9 ns 0.2708 568 B
GetHostReflection 973.0 ns 78.35 ns 231.01 ns 911.5 ns 0.1984 416 B

 

Now the graphic will help:

 

Interesting , right ?

Reflection = as normal –  is the slowest one. But the difference between RSCG switch and RSCG Dictionary is not too much…

Conclusion 1:  the feeling  was right at the end. But – the first result was deceiving

Conclusion 2: Creating a dictionary is more time consuming than a simple reflection for one property retrieved

Conclusion 3: I do prefer RSCG Dictionary vs RSCG switch – less work for me as a programmer and similar time results.

Conclusion 4: do not over engineer if you do not feel the need . For just one property, Reflection is better….

Conclusion 5: This is not final. I should also write the values of the properties . Maybe next time a new benchmark….

RecordVisitors–packages and thanks–part 12

Now it is time to see the outdated packages and say thanks to the people that help me created all this project.

For this I install https://github.com/KrystianKolad/DotnetThx . The result is https://github.com/ignatandrei/RecordVisitors/blob/main/src/RecordVisitors/thanks.md

Also, I have installed dotnet outdated tool ( new version!) to see what I should improve

Also , I have installed dotnet-project-licenses to let the user see the license for the project . You can see all the licences at https://github.com/ignatandrei/RecordVisitors/blob/main/src/RecordVisitors/licenses.txt 

Also, a tool that worth is  dotnet-depends – make a try ( allow to run several seconds). You will be impressed by Norton Commander interface…

The CI in github yml it is

– name: helper – see Outdated, thanks , licences and depends

run: |

    cd src/RecordVisitors/       

    dotnet dotnet-outdated

    dotnet dotnet-thx

    dotnet dotnet-project-licenses -i .\RecordVisitors.sln

    # dotnet dotnet-depends

RecordVisitors–Static Analysis with SonarCloud–part 11

Now I should see if the code that I have written is enough good. One way to determine is static analysis – and sonarclould.io is open for open source.

It was pretty simple to setup

1.  I have install the tool  dotnet-sonarscanner

2. Add the secrets from sonnarcloud to Github

3. Add JAVA to the CI Github Action

    – name: Set up JDK 11

uses: actions/setup-java@v1

with:

java-version: 1.11

4. run the build with

cd src/RecordVisitors

dotnet dotnet-sonarscanner begin /k:”ignatandrei_RecordVisitors” /o:”ignatandrei-github” /d:sonar.login=”${{ secrets.SONAR_TOKEN }}” /d:sonar.host.url=”https://sonarcloud.io”

dotnet build

dotnet dotnet-sonarscanner end /d:sonar.login=”${{ secrets.SONAR_TOKEN }}”

And now I have quality gate A –  see https://sonarcloud.io/dashboard?id=ignatandrei_RecordVisitors

RecordVisitors- Readme for Nuget–part 10

Nuget now allow for a package to see a Markdown document. I choose to embed the readme.md file

I just embed into csproj file:

<ItemGroup>
     <None Include=”../../../readme.md” Pack=”true” PackagePath=”\” />
   </ItemGroup>

<PropertyGroup>

<PackageReadmeFile>readme.md</PackageReadmeFile>

</PropertyGroup>

I needed also to make a small modif , to remove HTML comments that were seeing into nuget

$path = “../../README.md”

$path =Resolve-Path $path

Write-Host $path

$fileContent = gc $path

Write-Host $fileContent.Length

For($i=0;$i -lt $fileContent.Length ; $i++){

$line = $fileContent[$i];

If ($line -match ‘^<!–‘) {

$state=’comment’

$fileContent[$i] =$null # because `continue` doesn’t work in a foreach-object

      }

If ($line -match ‘–>$’) {

$state=’content’

$fileContent[$i] =$null

      }

If ($state -eq ‘comment’) {

$fileContent[$i] =$null

      }

}

$fileContent |Set-Content $path

You can see the end result at https://www.nuget.org/packages/recordvisitors#

RecordVisitors- BDD–part 9

It is recommended to have tests. And better is to  see the output of the tests in some readable format .

On other hand, I do not like full BDD   frameworks as SpecFlow – I think that are too overkill in order to achieve less.

So – something like https://github.com/LightBDD/LightBDD seems to fit the bill.

After you read the documentation, it is not so difficult. You transform test from

[Fact]
public async void TestFakeUser()
{

    // Arrange
    var client = _factory.CreateClient();

    // Act
    var response = await client.GetStringAsync("/recordVisitors/AllVisitors5Min");

    // Assert
    var str = "JeanIrvine";
    Assert.True(response.Contains(str), $"{response} must contain {str}");

}


into

HttpClient client;
string response;
private void Given_The_Application_Starts()
{
  StepExecution.Current.Comment("!!!Start application!!!!");
  client = _factory.CreateClient();
}

private async Task When_The_User_Access_The_Url(string url)
{
    response = await client.GetStringAsync(url);
}
private void Then_The_Response_Should_Contain(string str)
{            
    Assert.True(response.Contains(str), $"{response} must contain {str}");
}

[Scenario]
[ScenarioCategory("VisitorRecord")]
public async void TestFakeUser()
{
    await Runner
        .AddSteps(Given_The_Application_Starts)
        .AddAsyncSteps(
            _ => When_The_User_Access_The_Url("/recordVisitors/AllVisitors5Min")
        )
        .AddSteps(
            _ => Then_The_Response_Should_Contain("JeanIrvine")

        )
        .RunAsync();
                
}


Yes, it seems some more code – but you can re-use the functions and , more, the documentation looks great!

The results can bee seen at https://record-visitors.readthedocs.io/en/latest/BDD/LightBDDReport/

Friday Links 428

  • 4 Experts List Favorite New EF Core 5.0 Features — Visual Studio Magazine
  • Const Visualizer – Visual Studio Marketplace
  • Stack Trace Explorer – Visual Studio Marketplace
  • Chaos Engineering tools comparison
  • New C# Source Generator Samples | .NET Blog
  • roslyn-sdk/samples/CSharp/SourceGenerators at master � dotnet/roslyn-sdk
  • NuGet Multiplatform Library Projects (aka Nugetizer 3000) – Xamarin | Microsoft Docs
  • New C# Source Generator Samples | .NET Blog
  • kzu/ThisAssembly: Exposes project and assembly level information as constants in the ThisAssembly class using source generators powered by Roslyn.
  • lunet-io/scriban: A fast, powerful, safe and lightweight scripting language and engine for .NET
  • roslyn/source-generators.cookbook.md at master � dotnet/roslyn
  • How to generate code using Roslyn source generators in real world scenarios
  • Fit URLs
  • AntiPatterns | Blog
  • Alex Lakatos – 10 Things I Wish I d Learned Sooner After Being a Developer for 10 Years
  • NPM 7: This Is What I Call An Update | by Fernando Doglio | Nov, 2020 | Bits and Pieces
  • New RxJS Primitives release, new operators + Typescript 4 – Ideas and stuff
  • fiyazbinhasan/CoreFormatters: Some .NET Core Custom Formatters. Includes Yaml Input/Output Formatter, Pdf Output Formatter, Xlsx Output Formatter
  • scriban/language.md at master � lunet-io/scriban
  • The Book of the Runtime – The internals of the .NET Runtime that you won’t find in the documentation – Scott Hanselman’s Blog
  • 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.