I am creating a Windows service in C# on .NET Core 8. This is my first attempt. The service works, but I now want to remove some hardcoded elements and move the settings into my appsettings.json
file.
I've started with just a few settings.
My appsettings.Development.json
has the following content:
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"PlannedOutageConn": "Data Source=localhost;Initial Catalog=planned_outage;Persist Security Info=True;User ID=User;Password=password;Trust Server Certificate=True"
},
"Settings": {
"corsOrigin": "http://Server1",
"plannedOutageWebService": "https://localhost:7400/api/triview",
"verboseLogging": true
}
I have a SettingsConfig
class:
namespace PlannedOutageWindowsSvc.Models
{
/// <summary>
/// A class representing the various app configurations found in appsettings.{environment}.json
/// </summary>
public class SettingsConfig
{
/// <summary>
/// The origin host e.g. http://localhost or https://Server1
/// </summary>
public string corsOrigin { get; set; } = "";
/// <summary>
/// Api endpoint for Planned Outage Web Service
/// </summary>
public string plannedOutageWebService { get; set; } = "";
/// <summary>
/// Dictates if verbose logging is on or off (boolean)
/// </summary>
public bool verboseLogging { get; set; }
}
}
In my program.cs
, I have the following:
using App.WindowsService;
using Microsoft.EntityFrameworkCore;
using PlannedOutageWindowsSvc.Models;
using NLog;
using NLog.Web;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddWindowsService(options =>
{
options.ServiceName = "PlannedOutageWinSvc";
});
var env = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
IConfiguration config = new ConfigurationBuilder()
.AddJsonFile($"appsettings.json", false, true)
.AddJsonFile($"appsettings.{env}.json", true, true)
.AddEnvironmentVariables()
.Build();
// Early init of NLog to allow startup and exception logging, before host is built
var mlogger = NLog.LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
mlogger.Warn("PlannedOutageWindowsService is being initiated");
try
{
//DB connection/context
builder.Services.AddDbContext<planned_outageContext>(
options => options.UseSqlServer(builder.Configuration.GetConnectionString("PlannedOutageConn")));
builder.Services.Configure<SettingsConfig>(config.GetSection("Settings"));
builder.Services.AddHostedService<WindowsBackgroundService>();
builder.Services.AddSingleton<PlannedOutageService>();
IHost host = builder.Build();
host.Run();
}
catch (Exception ex)
{
// NLog: catch setup errors
mlogger.Error(ex, "PlannedOutageWindowsService stopped because of exception");
throw;
}
finally
{
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
NLog.LogManager.Shutdown();
}
I now want to access my settings from appSettings.{env}.json
within my PlannedOutageService.cs
singleton class:
using PlannedOutageWindowsSvc.Models;
using System.Text.Json;
namespace App.WindowsService
{
public class PlannedOutageService()
{
private SettingsConfig svcSettings;
public PlannedOutageService(SettingsConfig? appSettings) //ERROR CS8863
{
svcSettings = appSettings;
}
public static async Task ProcessTriViewMessages()
{
//THIS IS WHERE I WANT TO ACCESS THE SETTINGS VALUES
string triviewUrl = svcSetting.plannedOutagesWebService; //ERROR CS0120
// ..... other code removed
}
}
}
The above code results in two errors that I cannot resolve:
CS8862 A constructor declared in a type with parameter list must have 'this' constructor initializer.
CS0120 An object reference is required for the non-static field, method, or property 'PlannedOutageService.svcSettings'
How do I access my settings in a singleton? Many thanks
You should use the Options pattern for settings. You registered them ok, but in order to get them via DI, you should wrap them into IOptions:
public PlannedOutageService(IOptions<SettingsConfig> appSettings)
{
// access settings via appSettings.Value, check for NULL if necessary
}
Your second problem has nothing to do with settings. You are trying to access an instance field in a static method, which would not work. It's static context, so you can only access static fields there, as there is no single instance available where you can red this data from.
UPDATE: You said you wanted to access your settings in the static context. Well, this won't work this way, because if you inject via DI, the value should be stored in an instance field. So you have two options here:
User method injection, i.e. just pass your settings into a method:
public static MyMesthod(SettingsConfig appSettings)
A better way even: do not use a static class to implement a singleton if you need instance variables and DI. There is a better way to achieve there is only one instance of the class. Just create a normal class-interface pair and register it as singleton:
services.AddSingleton<IPlannedOutageService, PlannedOutageService>();