My project is a Windows background service with .NET 8.
I want to use Microsoft Entity Framework Core in my current project. I also want to use the DbContextFactory
to create an instance where needed.
Therefore I need to set this up in my Program.cs
file.
Actually I need to explain a little bit more my current setup of my project. In appsettings.json
there is a property value, which controls which database should be used.
This is my appsettings.json
:
...
{
"AppSettings": {
"DaysBetweenExecution": 1,
"ExecutionHour": 3,
"ExecutionMinute": 0,
"Environment": "Dev"
},
...
As you can see here, the Environment is set to Dev
.
So I setup this AppSettings
in my Program.cs
like this:
...
builder.Services.AddOptions<AppSettings>()
.BindConfiguration("AppSettings");
...
to use these settings in my application.
The AppSettings
class looks like this:
namespace BrochureCreationService.ApplicationLogic.Models.AppSettings
{
public class AppSettings
{
public int DaysBetweenExecution { get; set; } = -1;
public int ExecutionHour { get; set; } = -1;
public int ExecutionMinute { get; set; } = -1;
public string Environment { get; set; } = "Dev";
}
}
This value Dev
is being handled by the class Environment
.
This is the Environment
class:
using Microsoft.Extensions.Options;
namespace BrochureCreationService.ApplicationLogic.Models.Enviroment
{
public class Environment : IEnvironment
{
private IOptions<AppSettings.AppSettings> _AppSettings;
public Enums.Environment Current { get; set; }
public Environment(IOptions<AppSettings.AppSettings> appSettings) {
_AppSettings = appSettings;
Current = GetEnvironmentFromAppSettings();
}
public Enums.Environment GetEnvironmentFromAppSettings()
{
switch(_AppSettings.Value.Environment.ToLower())
{
case "dev":
return Enums.Environment.Dev;
case "test":
return Enums.Environment.Test;
case "ittest":
case "it-test":
return Enums.Environment.ITTest;
case "live":
case "production":
return Enums.Environment.Live;
default:
return Enums.Environment.Dev;
}
}
public string GetConnectionString()
{
switch (_Environment.Current)
{
case Enums.Environment.Live:
return "connection string";
case Enums.Environment.Test:
return "connection string test";
case Enums.Environment.ITTest:
return "connection string it test";
case Enums.Environment.Dev:
return "connection string dev";
default:
return "connection string dev";
}
}
}
}
As you can see, the AppSettings
are injected in the constructor. This class provides me the correct connection string based on the Environment
setting in appsettings.json
.
Now my issue: I need to setup the factory for Entity Framework Core in Program.cs
.
I want to do it like this :
builder.Services.AddDbContextFactory<IMP_GG_Context>(options =>
{
var environment = new BrochureCreationService.ApplicationLogic.Models.Enviroment.Environment();
options.UseSqlServer(environment.GetConnectionString());
}, ServiceLifetime.Scoped);
The issue here is, that this line is not working:
var environment = new BrochureCreationService.ApplicationLogic.Models.Enviroment.Environment();
...because Environment
needs the AppSettings
or better IOptions<AppSettings.AppSettings>
in the constructor. How can I manage that in Program.cs
?
Or are there better ways to do this?
Maybe, if it helps. Here is my full current code of Program.cs
:
using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Logging.EventLog;
using BrochureCreationService.GUI;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddWindowsService(options =>
{
options.ServiceName = "Brochure Creation Service";
});
LoggerProviderOptions.RegisterProviderOptions<EventLogSettings, EventLogLoggerProvider>(builder.Services);
builder.Services.AddOptions<AppSettings>().BindConfiguration("AppSettings");
builder.Services.AddOptions<WordSettings>().BindConfiguration("WordSettings");
builder.Services.AddOptions<PdfSettings>().BindConfiguration("PdfSettings");
builder.Services.AddOptions<EmailSettings>().BindConfiguration("EmailSettings");
builder.Services.AddSingleton<IEnvironment, BrochureCreationService.ApplicationLogic.Models.Enviroment.Environment>();
builder.Services.AddSingleton<IServiceController, ServiceController>();
builder.Services.AddTransient<IExecution, Execution>();
//... some more dependencies with AddTransient
builder.Services.AddScoped<IIMP_GG_Context, IMP_GG_Context>();
builder.Services.AddDbContextFactory<IMP_GG_Context>(options =>
{
var appSettingsOptions = Configuration.GetSection("AppSettings").Get<AppSettings>();
var environment = new BrochureCreationService.ApplicationLogic.Models.Enviroment.Environment(appSettingsOptions);
options.UseSqlServer(environment.GetConnectionString());
}, ServiceLifetime.Scoped);
builder.Services.AddHostedService<WindowsBackgroundService>();
IHost host = builder.Build();
host.Run();
There is another overload for AddDbContextFactory
which you can inject IServiceProvider
, so easily it's possible to get the IEnvironment
service from DI:
builder.Services.AddDbContextFactory<ApplicationDbContext>((provider, options) =>
{
var environment = provider.GetRequiredService<IEnvironment>();
options.UseSqlServer(environment.GetConnectionString());
}, ServiceLifetime.Scoped);