C# bind whole appSettings file to class - c#

In C# we can bind some settings in appSettings to class, for example like that:
var connectionStrings = new ConnectionStrings();
var sectionConnectionString = Configuration.GetSection("ConnectionStrings");
In appsettings it looks like below:
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
And when I want to bind Logging I need to call another bind:
Configuration.GetSection("Logging");
How can I bind whole appsettings file? GetSection with empty string doesn't work:
Configuration.GetSection("");

You need a Class for your config and afterwards you can use this (You do not need to map every setting, just the ones you need):
var configObject = Configuration.Get<ConfigObject>();
Example config object:
public class ConfigObject {
public Logging Logging { get; set; }
public string AllowedHosts { get; set; }
public ConnectionStrings ConnectionStrings { get; set; }
}
public class Logging {
public LogLevel LogLevel { get; set; }
}
public class LogLevel {
public string Default { get; set; }
}
public class ConnectionStrings {
public string ConnString1 { get; set; }
}
Hint:
if you're not using aspnetcore you probably need to also include this NuGet package: Microsoft.Extensions.Configuration.Binder

You can use the Configuration instance as it is.
You can bind the settings to a class:
var appSettings = Configuration.Get<AppSettings>();
Or you can inject the settings with the Options pattern
services.Configure<AppSettings>(Configuration);

Related

Read from appsettings.json config file

I have the following config in my appsettings.json File
{
"AppSettings": {
"MainUser": {
"Name": "Urs",
"Surname": "Barandun",
"SesstionTimeInMin": "30",
}
},
And I'd like to create a custom configuration class and bind it. Eg:
MainUserConfiguration mainUserConfiguration = new MainUserConfiguration();
var configSection = config.GetSection("MainUserConfiguration");
configSection.Bind(mainUserConfiguration);
But, my code does not work. :/
Based on what you've provided, your appsettings file and the nested JSON for your configuration don't match up. A typical ASP.NET Core appsettings.json doesn't have a nested element called "AppSettings", and further you're getting a configuration section called "MainUserConfiguration" while your actual JSON just names that section "MainUser".
appsettings example:
{
"MyFirstClass": {
"Option1": "some string value",
"Option2": 42
},
"MySecondClass": {
"SettingOne": "some string value",
"SettingTwo": 42
}
}
In your code:
public class MyFirstClass
{
public string Option1 { get; set; }
public int Option2 { get; set; }
}
public class MySecondClass
{
public string SettingOne { get; set; }
public int SettingTwo { get; set; }
}
In your Startup.cs (presuming that's where you're accessing it with a defined Configuration object:
var myFirstClass = Configuration.GetSection("MyFirstClass").Get<MyFirstClass>();
var mySecondClass = Configuration.GetSection("MySecondClass").Get<MySecondClass>();
Console.WriteLine($"The answer is always {myFirstClass.Option2}");

How to validate config values from appSettings.json file automatically during DI setup?

I added configurations to the appSettings.json file in my .NET Core project. For the sake of simplicy I'm taking database settings as an example. So in the settings file you would have
{
"Database": {
"Host": "localhost",
"Port": 1234,
"Database": "myDb",
"Username": "username",
"Password": "pw",
"EnablePooling": true
}
}
When configuring the services in the Startup.cs file I want to make those settings accessible via dependency injection. The data model for this is
public class DatabaseSettings
{
public string Host { get; set; }
public ushort Port { get; set; }
public string Database { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public bool EnablePooling { get; set; }
}
and I configure it this way
private void SetupSettings(IServiceCollection services)
{
ServiceProvider serviceProvider = services.BuildServiceProvider();
IConfiguration configuration = serviceProvider.GetService<IConfiguration>();
IConfigurationSection databaseConfigurationSection = configuration.GetSection("Database");
services.Configure<DatabaseSettings>(databaseConfigurationSection);
}
Lastly I want to validate those settings. I know that I can create a validator class implementing the IValidateOptions interface.
public class DatabaseSettingsValidator : IValidateOptions<DatabaseSettings>
{
private readonly IList<string> failures;
public DatabaseSettingsValidator()
{
failures = new List<string>();
}
public ValidateOptionsResult Validate(string databaseSettingsName, DatabaseSettings databaseSettings)
{
if (databaseSettings == null)
failures.Add($"{databaseSettingsName} are required.");
if (string.IsNullOrEmpty(databaseSettings?.Host))
failures.Add($"{nameof(databaseSettings.Host)} must not be empty.");
if (string.IsNullOrEmpty(databaseSettings?.Database))
failures.Add($"{nameof(databaseSettings.Database)} must not be empty.");
if (failures.Any())
return ValidateOptionsResult.Fail(failures);
return ValidateOptionsResult.Success;
}
}
but do I have to create this class and call the Validate method on my own? Maybe there is something like this sample code?
.
services.ValidateConfiguration<IOptions<DatabaseSettings>, DatabaseSettingsValidator>();
So you pass in the configured settings and the validator to use.
but I'm struggling with two questions:
Is there a way I can collect all failures instead of returning after
one? So you would get a list of failures instead of having to fix one
by one.
Do I have to create this class and call the Validate method on my own?
Maybe there is something like this sample code?
services.ValidateConfiguration<IOptions,
DatabaseSettingsValidator>(); So you pass in the configured settings
and the validator to use.
Yes, we could collect all failures list and display them at once, and we could also create a class which contains the Validate method. Please check the following steps:
First, since the class name is "DatabaseSettings", it better sets the config section name as the same as the class name:
{
"DatabaseSettings": {
"Host": "localhost",
"Port": 1234,
"Database": "myDb",
"Username": "username",
"Password": "pw",
"EnablePooling": true
}
}
[Note] If using a different name, the value might not map to the Database Setting class, so when validate the data, they all null.
Second, using the Data Annotations method adds validation rules to the model properties.
public class DatabaseSettings
{
[Required]
public string Host { get; set; }
[Required]
public ushort Port { get; set; }
[Required]
public string Database { get; set; }
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
[Required]
public bool EnablePooling { get; set; }
}
Third, create a ServiceCollectionExtensions class which contains the ConfigureAndValidate method:
public static class ServiceCollectionExtensions
{
public static IServiceCollection ConfigureAndValidate<T>(this IServiceCollection #this,
IConfiguration config) where T : class
=> #this
.Configure<T>(config.GetSection(typeof(T).Name))
.PostConfigure<T>(settings =>
{
var configErrors = settings.ValidationErrors().ToArray();
if (configErrors.Any())
{
var aggrErrors = string.Join(",", configErrors);
var count = configErrors.Length;
var configType = typeof(T).Name;
throw new ApplicationException(
$"Found {count} configuration error(s) in {configType}: {aggrErrors}");
}
});
}
Then, register the ConfigureAndValidate service:
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureAndValidate<DatabaseSettings>(Configuration);
}
Finally, get the Exception list.
public class HomeController : Controller
{
private readonly DatabaseSettings_settings;
public HomeController(IOptions<DatabaseSettings> settings)
{
_settings = settings.Value; // <-- FAIL HERE THROW EXCEPTION
}
}
Then, test result like this (I removed the Host and Username from the appSettings.json):
More detail information, you can check this blog:Validating configuration in ASP.NET Core
ValidateOptions are mainly for complex scenario, the purpose of using ValidateOptions is that you can move the validate logic out of startup.
I think for your scenario, you can use below code as a reference
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions<MyConfigOptions>()
.Bind(Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations()
.Validate(config =>
{
if (config.Key2 != 0)
{
return config.Key3 > config.Key2;
}
return true;
}, "Key3 must be > than Key2."); // Failure message.
services.AddControllersWithViews();
}
For more details, please refer to this document
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-3.1#options-validation

Config Values are null when trying to access them using GetSection in startup file.(IOptions-config retrieval)

I'm not able to receive the config values from appsettings file in .NET Core.
appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"PortedConfig": {
"ConfigTableAccess": "ConfigTableConnectionString",
"ConfigTableName": "Config"
}
}
startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
var config = Configuration;
var settings = Configuration.GetSection("PortedConfig").Get<PortedConfig>();
services.Configure<PortedConfig>(options => Configuration.GetSection("PortedConfig").Bind(options));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddScoped<IEncryptManager, EncryptManager>()
.AddScoped<IDecryptManager, DecryptManager>();
}
PortedConfig.cs:
public class PortedConfig
{
public string ConfigTableAccess;
public string ConfigTableName;
}
In startup.cs, at runtime the configuration is getting populated but when I use GetSection the values are null as shown in images below:
As you can see in images above ConfigTableAccess and ConfigTableName are null in settings variable.
Change the fields to properties and don't forget a public setter.
public class PortedConfig
{
public string ConfigTableAccess { get; set; }
public string ConfigTableName { get; set; }
}

Correct way to initialize settings in .net core 2.2?

I created an REST api application which has many settings and stored in database. These settings are used during filtering and inserting data to the table.
Because I need to access settings every time I need to insert data. Instead of accessing settings from database, I created a global settings class and I put every settings in that class.
public static class GlobalSettings
{
public static string Setting_1;
public static string Setting_2;
public static string Setting_3;
public static string Setting_4;
public static void Initialize(ISettingsRepo repo)
{
try
{
var settings = new GSettings(repo);
Setting_1 = settings.SetSetting_1();
Setting_2 = settings.SetSetting_2();
Setting_3 = settings.SetSetting_3();
Setting_4 = settings.SetSetting_4();
}
catch (Exception ex)
{
throw new Exception("Error when loading settings.\r\n" + ex.Message);
}
}
}
Here ISettingsRepo is scoped service that will load the settings from database. The functions will initialize the settings to the properties.
Now to initialize GlobalSettings I used configure method in startup class like this.
using (var scope = app.ApplicationServices.CreateScope())
{
Settings.GlobalSettings.Initialize(scope.ServiceProvider
.GetRequiredService<Data_Repo.Settings.ISettingsRepo>());
}
Now I can use this in controller or anywhere in my api and get settings without accessing database. Also I can reload the GlobalSettings any time if settings are updated. But does this method correct way or has memory leak problems?
Is there any better method to do this.?
Example
My appsetting.json have structure like this.
"EmailSettings": {
"MailServer": "",
"MailPort": ,
"Email": "",
"Password": "",
"SenderName": "",
"Sender": "",
"SysAdminEmail": ""
},
I will define my class like this
public class EmailSettings
{
public string MailServer { get; set; }
public int MailPort { get; set; }
public string SenderName { get; set; }
public string Sender { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string SysAdminEmail { get; set; }
}
So we have the the config structure. The last thing we need is register inside Startup.cs
services.Configure<EmailSettings>(configuration.GetSection("EmailSettings"));
To use it inside service class
private readonly IOptions<EmailSettings> _emailSetting;
public EmailSender(IOptions<EmailSettings> emailSetting)
{
_emailSetting = emailSetting;
}
email.From.Add(new MailboxAddress(_emailSetting.Value.SenderName, _emailSetting.Value.Sender));

How to bind configuration objects from appsettings.json to user objects correctly

I started some basic project on .net Core 1.1,
and I wish to map some properties from appsettings.json to object, but I probably can't understand correct name convention or something pretty basic
Regarding MSDN Using Options and configuration objects section,
it is very easy to use it.
I added next lines to appsettings.json
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"XXXOptions": {
"X1": {
"AppId": "11",
"AppCode": "22"
},
"X2": {
"AppId": "",
"AppCode": ""
}
}
}
I added custom class
public class XXXOptions
{
public XXXOptions()
{
}
public X1 X1{ get; set; }
public X2 X2{ get; set; }
}
public class X1
{
public int AppId { get; set; }
public int AppCode { get; set; }
}
public class X2
{
public int AppId { get; set; }
public int AppCode { get; set; }
}
I added next code to Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Adds services required for using options.
services.AddOptions();
// Register the IConfiguration instance which MyOptions binds against.
services.Configure<XXXOptions>(Configuration);
// Add framework services.
services.AddMvc();
}
public class XXXController : Controller
{
private readonly XXXOptions _options;
public XXXController(IOptions<XXXOptions> optionsAccessor)
{
_options = optionsAccessor.Value;
}
public IActionResult Index()
{
var option1 = _options.X1;
return Content($"option1 = {option1.AppCode}, option2 = {option1.AppId}");
return View();
}
}
optionsAccessor.Value - Value containes null values at XXXController constructor.
but it seems like framework show mappet values at JsonConfigurationProvider inside of Configuration property
any ideas?
In ConfigureServices method change:
services.Configure<XXXOptions>(Configuration);
to
services.Configure<XXXOptions>(Configuration.GetSection("XXXOptions"));

Categories