Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.Net Core 3 dependency injection of a Serilog

I am doing a Windows Service where I use Serilog

this is fine in the worker:

public class Worker: BackgroundService
{
    private readonly ILogger<Worker> _logger;

    private readonly IConfiguration _configuration;
    private readonly string _connectionString;
    private bool _serviceIsStarting = true;

    public Worker(ILogger<Worker> logger, IConfiguration iConfig)
    {
        _logger = logger;
        _configuration = iConfig;
        _connectionString = _configuration.GetConnectionString("DestinationDatabase");
    }

    //....

}

My Program.cs contains this:

public static IHostBuilder CreateHostBuilder(string[] args)
{
    return Host.CreateDefaultBuilder(args)
        .UseWindowsService()
        .UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration
            .ReadFrom.Configuration(hostingContext.Configuration))
        .ConfigureServices((hostContext, services) =>
        {
            services.AddHostedService<Worker>();
        });
}

Everything is fine but then I want to inject a logger to my custom class that the Worker instance uses:

public class DataAccess
{
    private readonly string _connectionString;

    private readonly ILogger<DataAccess> _logger;

    public DataAccess(ILogger<DataAccess> logger, string connectionString)
    {
        _connectionString = connectionString;
        _logger = logger;
    }

    //.....
}

My problem is that I do not know how to instantiate my class DataAccess in the worker or to the dependency injection here.

Here is a [simplified] verison of my class:

using Microsoft.Extensions.Logging;
using Dapper;

public class DataAccess
{
    private readonly string _connectionString;
    private readonly ILogger<DataAccess> _logger;

    public DataAccess(ILogger<DataAccess> logger, string connectionString)
    {
        _logger = logger;
        _connectionString = connectionString;
    }
    public void SaveInspection(InspectionElm inspection)
    {
        using IDbConnection connection = new System.Data.SqlClient.SqlConnection(_connectionString);
        connection.Open();

        var p = new DynamicParameters();
        p.Add("@RodNumber", inspection.RodNumber);
            p.Add("@InspectionDate", Convert.ToDateTime(inspection.InspectionDate));
            p.Add("@MeasurementEnd", Convert.ToDateTime(inspection.MeasurementEnd));
            p.Add("@InspectionResultPass", Convert.ToBoolean(inspection.InspectionResultPass));;

        connection.Execute("InsertTindasjaMasterRecord", p, commandType: CommandType.StoredProcedure);

        _logger.LogInformation("Log something...");
    }

    public void InitMesureResultTypes()
    {
        using IDbConnection connection = new System.Data.SqlClient.SqlConnection(_connectionString);
        connection.Open();
        connection.Execute("InitMeasureResultType", null, commandType: CommandType.StoredProcedure);

        _logger.LogInformation("Log something...");
    }
}
like image 252
Ægir Örn Sveinsson Avatar asked Mar 13 '20 16:03

Ægir Örn Sveinsson


2 Answers

My problem was utter confusion about the general principles of Dependency Inversion and understanding that I needed to get rid of code like this:

var db = new DataAccess(_connectionString);

So I finally realized what needs to be done to have this working properly is this:

Program.cs:

public static IHostBuilder CreateHostBuilder(string[] args)
    {
        return Host.CreateDefaultBuilder(args)
            .UseWindowsService()
            .UseServiceProviderFactory(new AutofacServiceProviderFactory())
            .UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration
                .ReadFrom.Configuration(hostingContext.Configuration))
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<Worker>();
                services.AddScoped<IDataAccess, DataAccess>();        
            });
    }

Worker.cs:

public Worker(ILogger<Worker> logger, IConfiguration iConfig, IDataAccess dataAccess)
    {
        _logger = logger;
        _configuration = iConfig;
        _dataAccess = dataAccess;
    }

DataAcess.cs:

public class DataAccess: IDataAccess
{
    private readonly string _connectionString;
    private readonly ILogger<DataAccess> _logger;

    public DataAccess(ILogger<DataAccess> logger, IConfiguration iConfig)
    {
        _connectionString = iConfig.GetConnectionString("DestinationDatabase");
        _logger = logger;
    }
}

Things work perfect now and the only thing that I need to research is what method is the right one to use in .ConfigureServices:

services.AddScoped<IDataAccess , DataAccess>();

or

services.AddTransient<IDataAccess , DataAccess>();

or

services.AddSingleton<IDataAccess , DataAccess>(); 

At least I know now that AddScoped() works OK.

like image 122
Ægir Örn Sveinsson Avatar answered Sep 21 '22 06:09

Ægir Örn Sveinsson


Hopefully you find this helpfully.

First of all the right place to register dependencies is within the ConfigureServices of the StartUp class. Then the framework takes the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

You can register the DataAccess within the injection container by using the following code snippet(as @JamesFaix wrote in his comment): services.Add<DataAccess>()

Also as described in Microsoft's article Service lifetimes within documentation, services have a certain lifetime which can either be:

  • Transient: Transient lifetime services (AddTransient) are created each time they're requested from the service container. This lifetime works best for lightweight, stateless services

  • Scoped: Scoped lifetime services (AddScoped) are created once per client request (connection).

  • Singleton: Singleton lifetime services (AddSingleton) are created the first time they're requested (or when Startup.ConfigureServices is run and an instance is specified with the service registration).

    For further understanding on registering services and dependency injection take a look at the following article: Dependency injection in ASP.NET Core

Besides that and if you want a path more practical and understandable (in my opinion) for you to follow I am leaving this here: Dependency Injection in ASP.NET Core MVC

like image 25
vasilisdmr Avatar answered Sep 21 '22 06:09

vasilisdmr