Preprocessing configurations file before using properties - c#

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?

Related

cannot read values for few keys from local.settings.json file

I am unable to read the few of the keys from local.settings.json file. Below is the file content.
{
"IsEncrypted": false,
"Values": {
"KeyVaultUrl": "https://mykeyvault.azure.net/",
"SecretKey": "myconnectionstring",
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
I used the below code to read these values
var keyVaultUrl = Environment.GetEnvironmentVariable("KeyVaultUrl"); // returns null
var secretKey = Environment.GetEnvironmentVariable("SecretKey"); // returns null
var sample = Environment.GetEnvironmentVariable("AzureWebJobsStorage"); // returns "UseDevelopmentStorage=true"
I am not sure why it returns null for the key I have added. I have set Copy Always in Copy to Output Directory and Build Action to None.
Please assist.
Values you are trying to read, are not written in environment variables. To use config files, use IConfiguration. It can contain environment variables, multiple config files and much more.
It depends on type of your application, if you are using simple console application without hosts and builders, use this, but if you are using some kind of framework use this approach.
An environment variable is a variable whose value is set outside the program, typically through functionality built into the operating system!
Your data in appsettings is just key-value pairs of configuration settings for your application (from it's name - appsettings).
To access data in your appsettings you need to inject IConfiguration to your service (or controller or whatever)
private readonly IConfiguration _configuration;
public ServiceConstructor(public IConfiguration)
=> _configuration = configuration;
and then you can read values from there like this:
_configuration["IsEncrypted"]
and
_configuration["Values:KeyVaultUrl"]

Does development environment on Net Core 6 implies that it reads configurations from appsettings.Development first?

Good morning guys, just a simple question regarding this little builder snippets in Program.cs.
builder.Services.AddDbContext<SteelworksContext>(options =>
{
if (builder.Environment.IsDevelopment())
{
options.UseSqlServer(builder.Configuration.GetConnectionString("SteelworksDev")); //from appsettings.Development
}
else
{
options.UseSqlServer(builder.Configuration.GetConnectionString("Steelworks")); //from appsettings
}
});
Does that code implies that if condition is true the connection string is read from appsettings.Development or it just reads from appsettings as it normally does?
It depends on how the config settings have been loaded into the program but that would be a reasonable assumption to make.
You should look at the code that loads the config settings (this might be in a Startup class but will depend on your project).
FYI: It looks like the connection string has a different name in the appsettings & appsettings.Development config files. This is unnecessary and somewhat defeats the point of having the two files. Both files should just have the same connection string name e.g. "Steelworks" and only one line of code that sets up the DbContext:
options.UseSqlServer(builder.Configuration.GetConnectionString("Steelworks"));

Using Options Pattern in program.cs

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.

Issue understanding a behaviour linked to appsettings binding in .NET Core

I'm facing a weird issue where I can't seem to bind appsettings values from my appsettings.Development.json file using Configuration.Get<T>().
I've done it countless times on different project in the exact same way but apparently "not this time".
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var myConfig = Configuration.Get<MySettingType>();
}
}
Usually this works perfectly.
I left the Program.cs clean from any edit that could change the default behaviour regarding loading the appsettings files.
MySettingType is the same name as the key in the appsettings file, matching class is using properties, all public.
The configuration is at the root of the json file as well.
What happened is that the code would only lead to null values being filled in the myConfig object.
Now I solved the issue using this code instead of Configuration.Get<T>():
var myConfig = new MySettingType();
Configuration.GetSection(nameof(MySettingType)).Bind(myConfig);
This is all cool, but the question is why did I have to use that?
What could explain it?
I would always include a GetSection. Something like this:
Configuration.GetSection(nameof(MySettingType)).Get<MySettingType>();
But do you really need the myConfig later in your ConfigureServices method?
If not, you can also do this:
services.Configure<MySettingType>(Configuration.GetSection(nameof(MySettingType));
You can find different examples of how Microsoft suggest to bind hierarchical configuration.
When binding options, it is assumed that the structure of T will be the same as whatever the Configuration is. It does not take the type name into account. So if you want to bind the options from a specific part of the configuration keyed by your type, you'll need to use GetSection which will return a new ConfigurationSection object scoped to that section. Then the same rules apply since.
If you want a convention, you could write an extension method that could bind based on type names for you.
public static T BindByTypeName<T>(this IConfiguration configuration)
where T : class, new()
{
return configuration.Bind<T>(typeof(T).Name);
}
You could also wrap MySettingsType with another AppSettings or something so that your options match your appsettings.json structure.
class AppSettings
{
public MySettingsType MySettingsType { get; set; }
}
Then you can simply call Get<AppSettings> on the root configuration.
Configuration.Get<AppSettings>().MySettingsType;

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