Dependency Injection: Singleton, Scoped and Transient

The three lifetime registrations for the out of the box DI functionality:

  • singleton (one and only forever)
  • scoped (one in every request)
  • transient (new everytime).

In the code below three objects are added to the DI container, a singleton, a scoped and a transient item.

When you run this code you can observe that the singleton is always the same date (even on different browser tabs). The scoped object will have the same date during the request, even when retrieved multiple times from the DI container it will have the same date and time. The transient object however will have a different date and time each time it is retrieved from the DI container.

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder();

builder.Services.AddSingleton(x => new SingletonDate());
builder.Services.AddTransient(x => new TransientDate());
builder.Services.AddScoped(x => new ScopedDate());

var app = builder.Build();

app.Use(async (context, next) =>
{
    var single = context.RequestServices.GetService<SingletonDate>();
    var scoped = context.RequestServices.GetService<ScopedDate>();
    var transient = context.RequestServices.GetService<TransientDate>();

    await context.Response.WriteAsync("Open this page in two tabs \n");
    await context.Response.WriteAsync("Keep refreshing and you will see the three different DI behaviors\n");
    await context.Response.WriteAsync("----------------------------------\n");
    await context.Response.WriteAsync($"Singleton : {single.Date.ToString("MM/dd/yyyy hh:mm:ss.fff tt")}\n");
    await context.Response.WriteAsync($"Scoped: {scoped.Date.ToString("MM/dd/yyyy hh:mm:ss.fff tt")}\n");
    await context.Response.WriteAsync($"Transient: {transient.Date.ToString("MM/dd/yyyy hh:mm:ss.fff tt")}\n");
    await next.Invoke();
});

app.Run(async (context) =>
{
    await Task.Delay(3000);//delay for 100 ms

    var single = context.RequestServices.GetService<SingletonDate>();
    var scoped = context.RequestServices.GetService<ScopedDate>();
    var transient = context.RequestServices.GetService<TransientDate>();

    await context.Response.WriteAsync("----------------------------------\n");
    await context.Response.WriteAsync($"Singleton : {single.Date.ToString("MM/dd/yyyy hh:mm:ss.fff tt")}\n");
    await context.Response.WriteAsync($"Scoped: {scoped.Date.ToString("MM/dd/yyyy hh:mm:ss.fff tt")}\n");
    await context.Response.WriteAsync($"Transient: {transient.Date.ToString("MM/dd/yyyy hh:mm:ss.fff tt")}\n");
});

app.Run();

public class SingletonDate
{
    public DateTime Date { get; set; } = DateTime.Now;
}

public class TransientDate
{
    public DateTime Date { get; set; } = DateTime.Now;
}

public class ScopedDate
{
    public DateTime Date { get; set; } = DateTime.Now;
}
Share

Dependency injection: register all classes that implement a specific interface

You can register all classes in a project (or folder) that implement a certain interface. See the code below how to do this.

using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder();

var type = typeof(IBootstrap);
var types = System.AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(x => x.GetTypes())
    .Where(p => type.IsAssignableFrom(p) && p.IsClass);

foreach (var p in types)
{
    var config = (IBootstrap)System.Activator.CreateInstance(p)!;
    config.Register(builder.Services);
}

var app = builder.Build();

app.Run(context =>
{
    var person = context.RequestServices.GetService<Person>()!;
    var greeting = context.RequestServices.GetService<Greeting>()!;

    return context.Response.WriteAsync($"{greeting.Message} {person.Name}");
});

app.Run();

public interface IBootstrap
{
    void Register(IServiceCollection services);
}

public class Registration1 : IBootstrap
{
    public void Register(IServiceCollection services)
    {
        services.AddTransient(x => new Person { Name = "Mahmoud" });
    }
}

public class Registration2 : IBootstrap
{
    public void Register(IServiceCollection services)
    {
        services.AddTransient(x => new Greeting { Message = "Good Morning" });
    }
}

public class Person
{
    public string Name { get; set; } = string.Empty;
}

public class Greeting
{
    public string Message { get; set; } = string.Empty;
}
Share

JWT Tokens

Below the basic operations to create and validate JWT tokens

using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;

public class JwtGenerator
{
    public static string GenerateSecureKey()
    {
        using var randomNumberGenerator = RandomNumberGenerator.Create();
        var key = new byte[32]; // 256 bits
        randomNumberGenerator.GetBytes(key);
        return Convert.ToBase64String(key);
    }

    public static string GenerateToken(string secret)
    {
        JwtSecurityTokenHandler tokenHandler = new();
        byte[] key = Encoding.ASCII.GetBytes(secret);

        SecurityTokenDescriptor tokenDescriptor = new()
        {
            Subject = new ClaimsIdentity(new Claim[]
            {
                // Add any claims you need here
                new Claim(ClaimTypes.Name, "username"),
            }),
            Expires = DateTime.UtcNow.AddMinutes(60),
            SigningCredentials = 
                new SigningCredentials(
                        new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
        };

        SecurityToken token = tokenHandler.CreateToken(tokenDescriptor);
        return tokenHandler.WriteToken(token);
    }

    public static ClaimsPrincipal DecodeAndValidateToken(string token, string key)
    {
        var tokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(key)),
            ValidateIssuer = false,
            ValidateAudience = false,
            ClockSkew = TimeSpan.Zero
        };

        var tokenHandler = new JwtSecurityTokenHandler();

        try
        {
            var claimsPrincipal = tokenHandler.ValidateToken(token, tokenValidationParameters, out var validatedToken);
            return claimsPrincipal;
        }
        catch (SecurityTokenException)
        {
            Console.WriteLine("Invalid token.");
            return null;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"An error occurred: {ex.Message}");
            return null;
        }
    }

    public static void Main()
    {
        string key = GenerateSecureKey();
        Console.WriteLine(key);

        Console.WriteLine();

        string token = GenerateToken(key);
        Console.WriteLine(token);

        var claimsPrincipal = DecodeAndValidateToken(token, key);
        foreach (var claim in claimsPrincipal.Claims)
        {
            Console.WriteLine($"Claim Type: {claim.Type}, Claim Value: {claim.Value}");
        }
    }
}

Share

Add StyleCop to your Visual Studio build

Add a file called Directory.Build.Props to your solution with the contents below.

<Project>
	<PropertyGroup>
		<TargetFramework>net7.0</TargetFramework>
	</PropertyGroup>

	<ItemGroup>
		<PackageReference Include="StyleCop.Analyzers" version="1.2.0-beta.435">
			<PrivateAssets>all</PrivateAssets>
			<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
		</PackageReference>
	</ItemGroup>
</Project>

Also add a .editorconfig to your solution to configure your code styling. The StyleCop analyzer and the .editorconfig file play together to configure your build process.

Share

Validating objects at creation time

If we have more complex validation logic, we could introduce a factory method and a Result object to handle the validations better:

public record Result<T>
{
    public bool IsSuccess { get; private init; }
    public T? Value { get; private init; }
    public string? ErrorMessage { get; private init; }

    private Result(){}

    public static Result<T> Success(T value) => new() 
    {
       IsSuccess = true, Value = value
    };
    public static Result<T> Failure(string errorMessage) => new()
    {
        IsSuccess = false, ErrorMessage = errorMessage
    };
}

Here we declare the generic Result record, so now let’s see how to create the factory method for the Money value object:

public record Money
{
    private static readonly IReadOnlyCollection<string> SupportedCurrencies = new[]{"USD", "EUR"};

    public decimal Amount { get; }
    public string Currency { get; }
    
    private Money(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }

    public static Result<Money> Create(decimal amount, string currency)
    {
        if(string.IsNullOrWhiteSpace(currency))
            return Result<Money>.Failure($"{nameof(currency)} cannot be null or whitespace.");

        if(!SupportedCurrencies.Contains(currency.ToUpperInvariant()))
            return Result<Money>.Failure($"'{currency}' is not supported.");
        
        return Result<Money>.Success(new(amount, currency));
    }
}

Instead of throwing exceptions (or simply returning False), we return a Failure result (with a specific message), allowing the caller to handle this in a cleaner fashion.

Share

c# async in constructors

In C#, constructors cannot be declared as async because they are special methods used for object initialization and cannot be awaited. However, you can use an asynchronous factory method or an initialization method to perform asynchronous operations during object creation. Here’s an example:

public class MyClass
{
    private MyClass()
    {
        // Private constructor to enforce the usage of factory method
    }

    public static async Task<MyClass> CreateAsync()
    {
        var instance = new MyClass();
        await instance.InitializeAsync();
        return instance;
    }

    private async Task InitializeAsync()
    {
        // Perform asynchronous initialization tasks here
        await Task.Delay(1000); // Example asynchronous operation
    }
}

In the above example, the constructor for MyClass is marked as private to enforce the usage of the CreateAsync factory method. The CreateAsync method creates an instance of MyClass, calls the private constructor, and then asynchronously initializes the object by calling the InitializeAsync method. You can perform any necessary asynchronous operations within the InitializeAsync method.

To create an instance of MyClass, you would use the CreateAsync method as follows:

var myObject = await MyClass.CreateAsync();

By using this approach, you can achieve asynchronous behavior during object construction in C#.

Share

Bootstrap Image dropdown

Below is a bootstrap fragment to setup a dropdown input with images.

<html>

<head>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <style>
        .img-dropdown {
            display: inline-block;
            vertical-align: middle;
            margin-right: 10px;
            width: 20px;
            height:20px;
        }
    </style>
</head>

<body>
    <div class="container">
        <div class="row">
            <div class="col">
                <div style="float:left">
                    <div class="dropdown">
                        <button class="btn btn-secondary" type="button" id="dropdownMenuButton"
                            data-bs-toggle="dropdown" aria-expanded="false">
                            <div id="languageButtonText">??</div>
                        </button>
                        <ul class="dropdown-menu" aria-labelledby="dropdownMenuButton" id="ulLanguages">
                            <li>
                                <a class="dropdown-item" href="#" data-value="DE">
                                    <img src="img/de.svg" alt="Image 1" class="img-dropdown">
                                    German
                                </a>
                            </li>
                            <li>
                                <a class="dropdown-item" href="#" data-value="FR">
                                    <img src="img/fr.svg" alt="Image 2" class="img-dropdown">
                                    French
                                </a>
                            </li>
                            <li>
                                <a class="dropdown-item" href="#" data-value="GB">
                                    <img src="img/gb.svg" alt="Image 3" class="img-dropdown">
                                    English
                                </a>
                            </li>
                            <li>
                                <a class="dropdown-item" href="#" data-value="NL">
                                    <img src="img/nl.svg" alt="Image 4" class="img-dropdown">
                                    Dutch
                                </a>
                            </li>
                            <li>
                                <a class="dropdown-item" href="#" data-value="Frisian">
                                    <img src="img/frisian.svg" alt="Image 5" class="img-dropdown">
                                    Frisian
                                </a>
                            </li>
                        </ul>
                    </div>
                </div>
                <div style="float:left">
                    <div id="selected-image" style="align-items: center; display:flex;margin-left:5px"></div>
                </div>
            </div>
        </div>
    </div>
</body>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js"
    integrity="sha512-3gJwYpMe3QewGELv8k/BX9vcqhryRdzRMxVfq6ngyWXwo03GFEzjsUm8Q7RZcHPHksttq7/GFoxjCVUjkjvPdw=="
    crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
    integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
    crossorigin="anonymous"></script>
<script>
    $(document).ready(function () {

        // When a dropdown item is clicked
        $('.dropdown-item').click(function () {
            var lang = $(this).data('value');
            var id = 'Language';
            $('#' + id).val(lang);

            // Get the image source of the clicked item
            var selectedImageSrc = $(this).find('img').attr('src');

            // Set the selected image in the div
            $('#selected-image').html('<img src="' + selectedImageSrc + '" alt="Selected Image" width="51">');
            $("#languageButtonText").html(lang);
        });

        $("#ulLanguages li a").filter("[data-value=" + "Frisian" + "]").trigger("click");

    });
</script>

</html>
Share

Add syntax highlighting to ASP.NET site

To enable syntax highlighting on your ASP.NET site follow the steps below:

  1. Goto prismjs.com and press the Download button.
  2. Add the next items to the default languages: C#, ASP.NET (C#), Bash + Shell + Shell, C#, Razor C# and SQL.
  3. Also add the plugin “line-numbers”.
  4. At the bottom of the page download the prims.js and prism.css files.

In the Shared/_Layout.cshtml add both the prism.js and prism.css file.

In the Index.cshtml view add some code to test it out

<div class="row">
    <div class="col line-numbers">
<pre><code class="language-csharp">public class HelloWorld
{
    public static void Main(string[] args)
    {
        Console.WriteLine("Hello, World!");
    }
}
</code>
</pre>
</div>
    <div class="col">
        <pre>
<code class="language-sql">
SELECT * FROM AspNetUsers u
LEFT JOIN AspNetRols on r.Id = u.Id
WHERE ID = 1;
</code>
    </pre>
    </div>
</div>

The resulting page will look like this

Share

VirtualMin error regarding can_use_gcloud_storage_creds 

After a fresh install of Ubuntu 22.04 and VirtualMin I tried to restore some WordPress sites to my new server. Created a backup on the old server. Next logged into the new server and in VirtualMin navigated to VirtualMin -> Backup and Restore -> Restore Virtual Servers. On this page I got the error as shown below.

HTTP/1.0 500 Perl execution failed Server: MiniServ/2.021 Date: Fri, 21 Apr 2023 07:11:21 GMT Content-type: text/html; Charset=utf-8 Connection: close

ERROR — PERL EXECUTION FAILED
Undefined subroutine &virtual_server::can_use_gcloud_storage_creds called at /usr/share/webmin/virtual-server/cloud-lib.pl line 304.

Not sure what causes this but it has something to do with the google cloud sdk not being installed on the server i suspect. I do not need this so I opend the cloud-lib.pl file in folder /usr/share/webmin/virtual-server/ and modified the perl code as a work around. Replace the function cloud_google_get_state with the definition below:

sub cloud_google_get_state
{
   return { 'ok' => 0 };
}

Next wait for a fix from VirtualMin.

Share