ASP.NET Core DI constructor with parameters

Karen Payne - Aug 10 - - Dev Community

Introduction

Most examples for dependency injection will register a parameter-less service such as presented below for registering a simple service. Here the lesson is how to register a service that require two parameters from, in this case from another registered service. The service was selected because the author had no instructions for ASP.NET Core and secondly appears to be a semi popular NuGet package.

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddRazorPages();

        builder.Services.AddScoped<IValidator<Person>, PersonValidator>();
        builder.Services.AddFluentValidationAutoValidation();

        var app = builder.Build();

        // Configure the HTTP request pipeline.
        if (!app.Environment.IsDevelopment())
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.MapRazorPages();

        app.Run();
    }
}

Enter fullscreen mode Exit fullscreen mode

Source code

🔸 Source code uses NET8 Core Framework

LocalStorage NuGet package

LocalStorage provides capabilities to stored data in a physical file which can be stored and accessed easily along with support for encrypting data plus storing information to construct the service in appsettings.json with user secrets.

🛡️ Before running the sample code, you need to initialize user secrets by right clicking on the project, select manage user secrets and copy/paste the following in and save the file.

{
  "SecretVault": {
    "Key": "@user_!maple_hiney_cars1",
    "Password": "^SsD_Ooops_Coffee_tree_d_f_5",
    "Salt": "50652cbc-cd7b-4763-a833-f2802a26285e"
  }
}
Enter fullscreen mode Exit fullscreen mode

Right click on the project in Solution Explorer to perform the above.

screenshot for above instructions

What it's not

Methods to work with web local storage.

Sample task

Take information collect in the index page and retrieve the information in another page.

Model

public class Person
{
    public int Id { get; set; }
    public string  FirstName { get; set; }
    public string LastName { get; set; }
    public override string ToString() => $"{Id,-4}{FirstName} {LastName}";
}
Enter fullscreen mode Exit fullscreen mode

Steps

Which are already coded but lets talk about the code. LocalStorage is the class which accepts parameters which will be registered.

The constructor for LocalStorage requires for the first parameter of type LocalStorageConfiguration which is included in the LocalStorage NuGet package and the second parameter is the password. Information/settings will be ready from appsettings.json as per the secret file above.

Since registering a service requires an interface the following is needed.

public interface ILocalSetup
{
    LocalStorageConfiguration Configuration { get; set; }
    string Key { get; init; }
    string Password { get; init; }
}
Enter fullscreen mode Exit fullscreen mode

The class which implements ILocalSetup is a singleton although a non singleton class may be used while a singleton is easier.

public sealed class LocalSetup : ILocalSetup
{
    private static readonly Lazy<LocalSetup> Lazy = new(() => new LocalSetup());
    public static LocalSetup Instance => Lazy.Value;
    public LocalStorageConfiguration Configuration { get; set; }
    public string Key { get; init; } 
    public string Password { get; init; }
    public static string Salt { get; set; }

    public LocalSetup()
    {
        Key = VaultReader.Key;
        Password = VaultReader.Password;
        Salt = VaultReader.Salt;

        Configuration = GetConfiguration();
    }

    private static LocalStorageConfiguration GetConfiguration() 
        => new() { EnableEncryption = true, EncryptionSalt = Salt };
}
Enter fullscreen mode Exit fullscreen mode

Register the service for ILocalSetup in Program.cs

builder.Services.AddSingleton<ILocalSetup, LocalSetup>();
Enter fullscreen mode Exit fullscreen mode

Once the service has been registered, its time to register ILocalStorage. ILocalSetup is needed which is the important part of the process.

GetRequiredService is used to get a reference to the prior service.

Using GetRequiredService, if the service needed is not found will cause a runtime exception while GetService will return null which means the developer must check for null.

/*
 * service to get configuration and password from appsettings.json
 * setup with user secrets (Azure is a better option).
 */
builder.Services.AddSingleton<ILocalSetup, LocalSetup>();

// service to read and write to local storage file
builder.Services.AddScoped<ILocalStorage>(provider =>
{
    var localSetup = provider.GetRequiredService<ILocalSetup>();
    return new LocalStorage(localSetup.Configuration, localSetup.Password);
});
Enter fullscreen mode Exit fullscreen mode

In regards to the last code block, localSetup has a reference to LocalSetup which has properties which are passed to the LocalStorage constructor.

Note that the LocalStorage package will also work without passing parameters but data will not be protected.

Using LocalStorage

In the sample code, left image is the input page, right is the receiver.

screenshot for input and receiver pages

C# page backends

Input/index page

  • LocalStorageKey variable taken from the singleton class contains a key from appsettings.json for passing to Store method in the Post which needs to match in the receiver page.
  • _localStorage field is set in the page constructor which is used in Post to save an instance of Person to a file.

To keep things to the point, there is no assertion to see if first and last name are not empty. Person.Id is harded coded as there is no reason in this sample to be concerned with incrementing the identifier.

using Hanssens.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using WorkingWithLocalStorageApp.Classes;
using WorkingWithLocalStorageApp.Models;


namespace WorkingWithLocalStorageApp.Pages;
public class IndexModel : PageModel
{
    string LocalStorageKey => LocalSetup.Instance.Key;
    private LocalStorage _localStorage;

    [BindProperty]
    public Person Person { get; set; }

    public IndexModel(ILocalStorage storage) 
        => _localStorage = storage as LocalStorage;

    public void OnGet() { }

    public RedirectToPageResult OnPostSetLocalToLocalStorage()
    {
        Person.Id = 1;
        _localStorage.Store(LocalStorageKey,Person);
        _localStorage.Persist();

        return RedirectToPage("ViewPage");
    }

    public void OnPostGetFromLocalStorage()
    {
        var person = _localStorage.Get<Person>(LocalStorageKey);

        Log.Information("Person: {person}", person);
    }
}
Enter fullscreen mode Exit fullscreen mode

The encrypted file.

contents of the local file

Receiving page

Uses the same key as in the input/input page to read the person data into a new instance of Person property and presents first and last name on the page.

public class ViewPageModel : PageModel
{
    private LocalStorage _localStorage;

    [BindProperty]
    public Person Person { get; set; }

    public ViewPageModel(ILocalStorage storage) 
        => _localStorage = storage as LocalStorage;

    public void OnGet()
    {
        Person = _localStorage.Get<Person>(LocalSetup.Instance.Key);
    }
}
Enter fullscreen mode Exit fullscreen mode

Summary

Code has been presented to register a service where the constructor required values from another registered service. By following along this will be helpful to be in any Microsoft C# developer's toolbox.

Also, code to get and save information which can be shared between sessions and/or pages.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .