Category: .NET Core

EventId in logging

Finally I have realized why it is necessary to have EventId logged and it is not the same life without him – especially in the microservices context.

So why it is not enough

public static void LogError (this Microsoft.Extensions.Logging.ILogger logger, Exception? exception, string? message, params object?[] args);

and I strongly recommend

public static void LogError (this Microsoft.Extensions.Logging.ILogger logger, Microsoft.Extensions.Logging.EventId eventId, string? message, params object?[] args);

?

  Let’s assume that you have something like a log aggregator , such as ElasticSearch

You want to see from the error message , at a glace the details of the problem – like the root cause , some additional data – without going into details of the object. Something like “Drive C:  too full to write”  or “Drive D:  too full to write”  .

Also, you want to see how many error of this kind occurs – how many “ too full to write “ have occured ?

You ca put the message like “ Drive too full to write – look into details ” – and count those – but the investigators will have to click on more time to find what drive it is.

Welcome to EventID – https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.eventid?view=dotnet-plat-ext-6.0 .

You can have a

var evt = new EventId(7000,”DriveTooFull” );

logger.LogError(evt, “Drive C: too full to write” )

Now you have the message detaisl and you can count how many 7000 ( or DriveTooFull ) errors you have

[NuGet]: HealthCheck

This is a Nuget that I use in all projects when I am making any ASP.NET Core prohect

Link: https://www.nuget.org/packages/AspNetCore.HealthChecks.UI/

Site: https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks 

What it does:  Implements health checks for your site and/or dependencies

Usage:

public void ConfigureServices(IServiceCollection services)
{

//code
     services.AddHealthChecks()
         .AddSqlServer(myConnectionString)
  }

[NuGet]: Polly

This is a Nuget that I use in all projects when I am making HTTP calls..

Link: https://www.nuget.org/packages/polly

Site: https://github.com/App-vNext/Polly

What it does:  Implements all kind of policies for retrying – see https://github.com/App-vNext/Polly/wiki/Transient-fault-handling-and-proactive-resilience-engineering

Usage: – copied from https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/implement-http-call-retries-exponential-backoff-polly

static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
     return HttpPolicyExtensions
         .HandleTransientHttpError()
         .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
         .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2,
                                                                     retryAttempt)));
}

Dependent Framework Versioning

There are multiple ways to version a software . I have used SemanticVersioning ( https://semver.org/ ) and Calendar Versioning (  https://calver.org/ )

Of course , there are others  – please read https://en.wikipedia.org/wiki/Software_versioning   – interesting versioning based on e or PI .

However , I want to propose a new standard :

Dependent Framework Versioning

The major version of a  package is the same of the major version of the framework that is installed on . For the others , can be calver or semver or any other – depending on your choice.

For example , I have made a BlocklyAutomation  package for .NET Core 3 – and it is versioned like

For .NET Core 3  –  https://www.nuget.org/packages/NetCore2Blockly/3.2022.224.16

For .NET Core 5https://www.nuget.org/packages/NetCore2Blockly/5.2022.210.2007

Why is this ? To be easy identified by the users of the package. If I have one user that have an app  on .NET Core 3 and other on .NET Core 5, how can they identify easy what is the latest version for this package  ? With this approach , this can be done just looking on the major version corresponding with the framework version (and yes, I use calver versioning for the rest – yyyy.Md.Hm)

[NuGet]:Scriban

This is a Nuget that I use in almost every .NET Core project to get the error details ( 500)

Link: https://www.nuget.org/packages/Scriban/

Site: https://github.com/scriban/scriban

What it does:  Like Razor –  a template intepreter

Usage:

var template = Template.Parse(@"
<ul id='products'>
  {{ for product in products }}
    <li>
      <h2>{{ product.name }}</h2>
           Price: {{ product.price }}
           {{ product.description | string.truncate 15 }}
    </li>
  {{ end }}
</ul>
");
var result = template.Render(new { Products = this.ProductList });

Using files in a ASP.NET Core nuget package

To use static (html) files in a NUGET ASP.NET Core package to be displayed I have used the following solution

1. Put the files in a folder ( in my case , blocklyAutomation )

2. Put in the .csproj to embed the files

<ItemGroup>
   <EmbeddedResource Include=”BlocklyAutomation\**\*”>
     <CopyToOutputDirectory>Never</CopyToOutputDirectory>
   </EmbeddedResource>
</ItemGroup>

3.  Create an extension to use it

;

public static void UseBlocklyUI(this IApplicationBuilder appBuilder)
{
    var manifestEmbeddedProvider =
            new ManifestEmbeddedFileProvider(Assembly.GetExecutingAssembly());
    var service = appBuilder.ApplicationServices;
    mapFile("blocklyAutomation", manifestEmbeddedProvider, appBuilder);


}

private static void mapFile(string dirName, IFileProvider provider, IApplicationBuilder appBuilder)
{
    var folder = provider.GetDirectoryContents(dirName);
    foreach (var item in folder)
    {
        if (item.IsDirectory)
        {
            mapFile(dirName + "/" + item.Name, provider, appBuilder);
            continue;
        }
        string map = (dirName + "/" + item.Name).Substring(dirName.Length);
        appBuilder.Map(map, app =>
        {
            var f = item;

            app.Run(async cnt =>
            {
                //TODO: find from extension
                //cnt.Response.ContentType = "text/html";
                using var stream = new MemoryStream();
                using var cs = f.CreateReadStream();
                byte[] buffer = new byte[2048]; // read in chunks of 2KB
                int bytesRead;
                while ((bytesRead = cs.Read(buffer, 0, buffer.Length)) > 0)
                {
                    stream.Write(buffer, 0, bytesRead);
                }
                byte[] result = stream.ToArray();
                var m = new Memory<byte>(result);
                await cnt.Response.BodyWriter.WriteAsync(m);
            });
        });
    }

}

4. That is all

Windows terminal + Powershell to run IDE, Angular, .NET Core

I work at http://github.com/ignatandrei/BlocklyAutomation – and every time I need to run Angular , Visual Studio Code IDE and .NET Core run ( and Visual Studio  sometimes  – it is more web based)

Also, sometimes I need to run in a container – to install globally something that I do not want to have in my PC.

So  I have 1 windows terminal command to run those :

First one

wt new-tab   –title RootSource –suppressApplicationTitle -p “Windows PowerShell” -d . cmd /k “cd src && code . && powershell” ;split-pane  –title Angular –suppressApplicationTitle -V -p “Windows PowerShell” -d . cmd /k “cd src &&  npm run start –watch && powershell” ;split-pane  –title LocalAPI_NetCore –suppressApplicationTitle -V -p “Windows PowerShell” -d . cmd /k “cd src/Local/LocalAPI/LocalAPI && dotnet watch run”

As you see , I have

code .

to run VSCode,

npm run start –watch

to run Angular and

dotnet watch run

to run dotnet.

Second one:

wt new-tab -p “Windows PowerShell” -d . ;split-pane -p “Windows PowerShell” -d . cmd /k “cd src && devcontainer open .”

The second it just works with devcontainer  – if you are interested in sources, see https://github.com/ignatandrei/BlocklyAutomation/tree/main/src/.devcontainer 

With those, I can start my developer environment fast ( how fast, that depends on my PC)

Python vs C#

I have read the article https://www.red-gate.com/simple-talk/development/dotnet-development/10-reasons-python-better-than-c-sharp

I think that deserves an answer –  so I take all points to answer to them. You can replicate the code in C# 10 / .NET 6 with a globalusings.cs with the following content:


global using static System.Console;
global using static System.Linq.Enumerable;
global using static System.IO.Directory;
global using static System.String;
global using static System.Math;

So let’s start:

Point 1 - Indented code blocks

//Point 1 - Indented code blocks
//With curly braces I can indent whatever way I want - I am not constrained

using System.Collections.Generic;

foreach (var i in Range(1, 11))
{
if (i % 3 == 0)
WriteLine("fizz");
else if (i % 5 == 0)
WriteLine("buzz");

}

Also , when I want to comment something , with C# I know when the else if is
terminating without counting tabs. Just see where the { is closing with }

Point 2 -  Self-declaring Variables

Seriously? What is the difference between Python
//meaning_of_life = 42
and C# ? Just a var ?

//Point 2 - Self-declaring Variables
var meaning_of_life = 42;
Point 3 -– Those modules

It is the same code as Python

foreach (var file in EnumerateFiles(@"C:\")){
 WriteLine(file);
}
Point 4 – Simplicity of Data Structures

Quoting : Purists will say that you need all the different data types that C# provides, but trust me, you don’t.
I want to think about how to modelate a CallCenter waiting for phones – Stack or Queue ?
I want to think about how to  modelate a CallCenter waiting for phones with business consumer that has priority ?https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.priorityqueue

Point 5 - Slicing

Same code in C#

var sins = new[]{ "pride", "envy", "gluttony", "greed", "lust", "sloth", "wrath" };
//var selected_sins = sins[1..(sins.Length-1)];
var selected_sins = sins[1..^1];
WriteLine(Join(" ", selected_sins));
Point 6 – For loops

Same syntax in C#

foreach (var i in Range(0,5))
{
WriteLine(i);
}
// words in a string
string[] words = { "Simple", "Talk" };
foreach (string word in words)
{
WriteLine(word);
}

Quoting : “(unlike in C#, where sometimes you use for and sometimes foreach)”
Please enumerate an array backwards in python without reversing

7 – List comprehensions

This is similar code in C#

var nr = Range(1, 11).Where(it => it % 2 == 0).Select(it => Pow(it, 3));
WriteLine(Join(" ", nr));
8 – Sets

Same example

//this contains some duplicates
var languages = new[] { "C#", "Python", "VB", "Java", "C#", "Java", "C#" };
//more dense than Python
languages = new HashSet<string>(languages).ToArray();
//this will give: ['C#', 'Java', 'Python', 'VB']
WriteLine(Join(" ", languages));

Second example

//create two sets: Friends characters and large Antarctic ice shelves
var friends = new[] { "Rachel", "Phoebe", "Chandler", "Joey", "Monica", "Ross" };
var ice_shelves = new[] { "Ronnie-Filchner", "Ross", "McMurdo" };
// show the intersection (elements in both lists)
WriteLine(Join(" ", friends.Intersect(ice_shelves)));
//show the union (elements in either list)
WriteLine(Join(" ", friends.Union(ice_shelves)));
// show the friends who aren't ice shelves
WriteLine(Join(" ", friends.Except(ice_shelves)));
// elements in either set but not both -- more difficult
WriteLine(Join(" ", friends.Union(ice_shelves).Except(friends.Intersect(ice_shelves))));
9 – Working with files and folders
//# list of 3 people
var people = new[] { "Shadrach", "Meshach", "Abednego" };
//# write them to a file
File.WriteAllLines(@"C:\all\a.txt", people);
10 – The quality of online help

See https://docs.microsoft.com/ – good tutorials

FileExtension–part 6 – CI/CD

So now how to make visible all those coding ?

For tests –CodeCov,  https://app.codecov.io/gh/ignatandrei/FileExtension hosts for free to see the coverage

For documentation – ReadTheDocs,  https://fileextension.readthedocs.io/en/latest/ hosts for free.

For packaging  – NuGEt https://www.nuget.org/packages/FileExtension/

For more detailed analysis Sonar https://sonarcloud.io/summary/overall?id=ignatandrei_FileExtension can show code smells and bugs and more

GitHub actions https://github.com/ignatandrei/FileExtension/actions allows me to run all those automatically and gather results

For showing a website – Azure  – https://fileextension.azurewebsites.net/ can show the usage.

I was thinking that it is enough for the people to understand the application

FileExtension–Tests–part 5

The tests are the easy part. I have put some files on a folder( https://github.com/ignatandrei/FileExtension/tree/master/src/TestFileExtensions/TestFiles ) and test if the extension matches the recognizer.

As NuGet packages I used

FluentAssertions

xunit

LightBDD.XUnit2

FluentAssertion allows me to write more readable tests , like 

AllExtensions.Should().HaveCountGreaterThanOrEqualTo(number);

LightBDD allows me to write tests in a more “business “ way :

await Runner
     .AddSteps(Given_The_Recognizer)
     .AddAsyncSteps(_ => When_Read_The_File(fileName))
     .AddSteps(
         _ => Then_Should_Recognize_File(fileName),
         _ => Then_The_Extension_Matches(fileName)
     )
     .RunAsync();

You can see the result at https://fileextension.readthedocs.io/en/latest/BDD/LightBDDReport/ = go down to see the Given / When /

Also, for enumerating files from the folder to test, I use ClassData

[ClassData(typeof(DirectoryTestData))]
public async void TestMultipleFiles(string nameFile)

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.