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...");
}
}
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With