Using Options Pattern in program.cs - c#

I am using the options pattern inside of my application, reading properties from my appsettings.json file and using them in classes throughout my application.
However, I have a case where I need to use these properties inside of my program.cs file itself. Here is code which does not work since I am not able to inject an instance of the config within the program.cs file. The code shows what I would like to happen... is there a better way of doing this and still staying within the options pattern?
builder.Services.Configure<SwaggerConfig>(builder.Configuration.GetSection("Swagger"));
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI(s =>
{
s.SwaggerEndpoint( SwaggerConfig.Url ,
SwaggerConfig.Name);
s.RoutePrefix = SwaggerConfig.RoutePrefix;
});

You have access to the IServiceProvider within your code. You can obtain the configuration you are looking for with app.Services.GetRequiredService<IOptions<SwaggerConfig>>().Value.
The thing is, once you call builder.Build(), the DI container will be also built.

Related

How to set NLog's internal LogFile programmatically for dependency injection

I know you can set NLog's internal log file programmatically like this:
NLog.Common.InternalLogger.LogFile = #"C:\Logs\nlog.log";
The above trick does not work for the following scenario, where I need to be able to set internal log file that's used by injected NLog. The one that gets created inside ASP.NET Core WebApi, using the following
.ConfigureLogging(logging =>
{
logging.SetMinimumLevel(LogLevel.Trace);
})
.UseNLog();
Are there any workarounds for me to programmatically configure LogFile and LogLeve of the NLog object that gets dependency injected?
Why not just remove internalLogFile="..." from NLog.{ASP_NET_CORE_ENVIRONMENT}.config ?
Or just re-assign InternalLogger.LogFile after having called LogManager.Setup().LoadConfigurationFromAppSettings() ?

C# AWS Parameter Store - Configuration not loading SystemManagerConfiguration in .Net 6

Application is not able to talk to AWS Parameter Store in .NET 6.
It is always talking to appsettings.json.
I tried debugging locally, still same behavior. Not able to find the SystemManagerConfiguration under list of configuration .
var builder = WebApplication.CreateBuilder();
var connectionString = builder.Configuration.GetConnectionString("OrderTrackerDatabase");
Packages Used
Library Source Code : https://github.com/aws/aws-dotnet-extensions-configuration
image
I got the same issue and finally resolved it.
The samples code in https://github.com/aws/aws-dotnet-extensions-configuration missed one line as below after called "AddSystemsManager" method in .Net 6.
builder.Services.Configure<Settings>(builder.Configuration.GetSection($"common:settings"));
After added above line, then I'm able to get the correct values from AWS Parameter Store when using the settings.
I've also created an issue of this in GitHub as below -
https://github.com/aws/aws-dotnet-extensions-configuration/issues/114
I believe the problem might be the trailing slash after "/OrderTracking/", try "/OrderTracking" instead.
WebApplication.CreateBuilder() will create new instance and doesn't carry over the SystemManager configuration.
Instead, use IConfiguration instance through constructor DI.
var connectionString = _configuration.GetConnectionString("OrderTrackerDatabase");
In my case this extensions method was returning null at my lambda:
private static IConfiguration InitializeConfiguration() => new ConfigurationBuilder()
.AddSystemsManager($"/my-data", true, TimeSpan.FromMinutes(5))
.Build();
Because the role of lambda didn't have permission for read SSM for that resource.
User: is not authorized to perform: ssm:GetParametersByPath on resource
So, just add necessary permission (ssm:GetParametersByPath) for the role of lambda to your resource at System Manager.
In my case, I am using lambda serverless, so the IConfig is always null when it is passed to the controller.
I resolved it by changing the IOptions<Settings> settings in the Controller constructor to IConfiguration settings and then access the parameters by name like _settings.GetValue<string>("ParameterName")
A little less "Object Oriented", but it seemed much easier than this complex solution

Preprocessing configurations file before using properties

In my appsettings.json file I have a couple of properties that need to be replaced at runtime.
Example :
"ConnectionStrings": {
"SqlConnection": "ReferenceToActualSqlConnection"
}
The appsettings file is loaded into a IConfiguration file and the value is used further
services.AddDbContext<DatabaseContext>(options =>
{
options.UseSqlServer(Configuration.GetSection("ConnectionStrings:SqlConnection").Value);
});
Except that before actually using it, I want to replace "ReferenceToActualSqlConnection" with the actual value in a separate module that has the IConfiguration file injected from the DI container, so the method above uses the replaced value. As far as I'm concerned the injection goes one way, so I can get the values in the preprocess module, but not get them updated back before usage. The module is registered as:
services.AddTransient<IConfigProcessor, ConfigProcessor>();
Any pointers on how to achieve this?

Is there a way to access appSettings.json globally?

I have a C# console app, and I'm defining an IConfiguration object as follows inside of Program.cs:
var myConfig = new ConfigurationBuilder()
.AddJsonFile($"appsettings.{myEnvironment}.json", true, true)
.Build();
... and I can reference settings from within that same Program.cs file like this:
var mySetting = myConfig.GetValue<string>("Section:SettingName");
However, I'm wondering what would be the way to access that same myConfig object from other files in my application? For instance, myConfig contains a connection string, but I don't actually make use of the connection string until a few layers deeper (inside of myService > myRepository > myDbContext). What is a best practice to read that connection string from inside of myRepository or myDbContext? Obviously, I can pass the connection string as a parameter from Program.cs and myService, but that seems inefficient.
You can use the build-in DI (Dependency Injection), which also works for .NET C# Console Applications as already mentioned here
How to use Dependency Injection in .Net core Console Application
Pass IConfiguration to your constructor. You can use Bind calls to create objects from it, or just read it like a dictionary.

How should I use appsettings.json config key/values in my ConfigurationServices ASP.NET Core 2.0 on startup?

I'm trying to configure my services for an ASP.NET Core 2.0 app/website.
I wish to reference some key/values from my appsettings.json file, in this method.
I'm not sure if what I'm going is OK or not:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddJsonFormatters()
.AddCors();
var applicationSettings = Configuration.GetSection("Settings").Get<ApplicationSettings>();
services.AddSingleton(applicationSettings);
// ** THIS IS WHAT I ORIGINALLY HAD, BUT IT'S ONLY SETTING
// THE VALUE IN DI/IOC.
//services.Configure<ApplicationSettings>(options => Configuration.GetSection("Settings")
// .Bind(options));
var foo = new Foo(applicationSettings.SomeSetting);
services.AddSingleton(foo);
}
See how i'm manually adding a singleton and then later, referring a value from the app settings instance?
vs
just configuring ...
So, is either way OK or is there a specific reason for either/or?
Remember -> i'm going to need to inject my settings into controllers, etc...
Technically, you can do either. In both cases, you have the configuration registered and available through dependency injection, so everything can depend on it and will get the configuration instance.
You are also using the centrally set up Configuration there, so you have all the benefits from the configuration stack there, e.g. multiple providers or environment specific overrides.
However, the favor has definitely moved to the IOptions way of consuming custom configuration. It’s the “state of the art” and used throughout ASP.NET Core for literally everything. It also allows you to switch to options that can be updated at runtime. That’s very powerful and might become useful eventually (not necessarily for your specific situation with the singleton, but maybe for something else).
It’s also really easy to set this up, actually shorter than what you tried:
services.Configure<ApplicationSettings>(Configuration.GetSection("Settings"));
services.AddSingleton<Foo>();
Note that, even for singletons, you shouldn’t explicitly create a new instance of it, but let DI handle that. If your class has the correct constructor, dependencies will be automatically injected anyway:
public class Foo
{
private readonly ApplicationSettings _settings;
public Foo (IOptions<ApplicationSettings> settings)
{
_settings = settings.Value;
}
}
Of course, Foo can also have more dependencies here. Since it’s going to be constructed by DI, you can just add more dependencies in the constructor, without having to update some new call somewhere.
If you need to configure certain services with settings that depend on your configuration, you still should not bind your configuration there directly. All of configuration is DI-based, so you just need to inject the right thing; a IConfigureOptions<T>. That’s basically the thing that provides the IOptions<T> to services later. In your JWT case, this could look like this:
// instead of passing an option configuration delegate here…
services.AddAuthentication().AddJwtBearer();
// … we register a IConfigureOptions<JwtBearerOptions> instead
services.AddSingleton<IConfigureOptions<JwtBearerOptions>, ConfigureJwtBearerOptions>();
// … ConfigureJwtBearerOptions could look like this:
class ConfigureJwtBearerOptions : IConfigureOptions<JwtBearerOptions>
{
private readonly ApplicationSettings _settings;
public ConfigureJwtBearerOptions(IOptions<ApplicationSettings> settings)
{
_settings = settings.Value;
}
public void Configure(JwtBearerOptions options)
{
// configure JwtBearerOptions here, and use your ApplicationSettings
options.MetadataAddress = _settings.JwtMetadataAddress;
}
}
This might seem unnecessarily verbose compared to just passing a delegate to AddJwtBearer() but note that this is exactly what happens under the hood when you pass that delegate: An IConfigureOptions<JwtBearerOptions> object will be created that calls your delegate in the Configure() call. So this is really just the same.
Note that for authentication schemes, you might actually set up a IConfigureNamedOptions<T> instead, which is almost the same thing except it can configure the options based on a name. For authentication schemes, that is the scheme name, so basically you check the scheme name in Configure() and then decide how to configure your options.
As for creating singleton instances, especially expensive ones, I would argue that ConfigureServices is the wrong place for such a thing. ConfigureServices is called very early in the application startup phase, when the whole DI infrastructure does not exist yet. So you could not rely on anything when creating your instance. I would also argue that it is still not your job to create the object but you should DI handle the creation of it and as such give it also control over its lifecycle.
If you absolutely need to control when the instance is created, I would suggest you to use the lifecycle events for this: Basically, after the application has set up properly but before a first request comes in, you request the instance of your services and initialize it. That way, you can still have it fully depend on DI, and it won’t be created lazily with the first request.
You can register lifecycle handlers in the Configure method:
public void Configure(IApplicationBuilder app, IApplicationLifetime applicationLifetime)
{
applicationLifetime.ApplicationStarted.Register(() =>
{
// application has started, request the singleton here to trigger DI to
// create the instance
app.ApplicationServices.GetService<ExpensiveSingleton>();
});
// …
}
}
Well the problem with that approach is that it will be impossible to load multiple configuration sections through DI. The Configuration API has many features, such as pluggable configuration provides, snapshots, etc.
I would suggest you at least use a class to bind you configuration section against, so DI can inject it based on its type. If you further down the line have need to another configuration class you won't run into issues.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration

Categories