IConfiguration.GetSection() as Properties returns null - c#

I'm trying to retrieve configuration inside my application.
I have passed the IConfiguration into the service class which needs to pull some settings.
The class looks a bit like this:
private IConfiguration _configuration;
public Config(IConfiguration configuration)
{
_configuration = configuration;
}
public POCO GetSettingsConfiguration()
{
var section = _configuration.GetSection("settings") as POCO;
return section;
}
In debug, I can see that _configuration does contain the settings
but my "section" is simply returned as null.
I'm aware that I could try to set up the Poco to be created in the startup, and then injected as a dependency, but due to the setup, I'd rather do it in separate classes from the injected IConfiguration, if possible.
My appsettings.json has these values:
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"settings": {
"Username": "user",
"Password": "password",
"Domain": "example.com",
"URL": "http://service.example.com"
}
}
My poco class looks like this:
public class POCO
{
public string URL { get; set; }
public string Username { get; set; }
public SecureString Password { get; set; }
public string Domain { get; set; }
}

You need to use:
var section = _configuration.GetSection("settings").Get<POCO>();
What's returned from GetSection is just a dictionary of strings. You cannot cast that to your POCO class.

IConfiguration and IConfigurationSection have an extension method Bind for this purpose:
var poco = new POCO();
_configuration.GetSection("settings").Bind(poco);
Or just Get
var poco = _configuration.GetSection("settings").Get(typeof(POCO));
Or generic Get
var poco = _configuration.GetSection("settings").Get<POCO>();

Related

C# bind whole appSettings file to class

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);

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

Read JSON as string from Microsoft.Extensions.Configuration

Microsoft.Extensions.Configuration has its own API for navigating through the JSON contained in the config file it reads in. (This is what ASP.NET uses for configuration)
For a given JSON node- is there a way to get access to its contents as a string rather than as more Configuration objects? I have JSON objects in my config file which I need to run through a JSON deserializer (so I just want to read this node from the file as a string).
Something akin to the following:
var myObjectsSection = configuration.GetSection("MyObjects");
var innerText = myObjectsSection.InnerText; //Is there any way to do this???
var myObjs = JsonConvert.DeserializeObject<MyObject[]>(innerText);
Config file:
{
"SomeSetting": "mySetting",
"MyObjects": [
{
...
},
{
...
}
]
}
Asp.net core 3 has a method for getting type-related configuration value: T IConfigurationSection.Get<T>()
I've tried to parse the custom configuration which you described and it is working.
appsetting.json:
{
"CustomSection": [
{
"SomeProperty": 1,
"SomeOtherProperty": "value1"
}
]
}
Startup class:
public class Startup
{
public Startup(IConfiguration configuration)
{
this.Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
IConfigurationSection section = this.Configuration.GetSection("CustomSection");
var configs = section.Get<List<CustomSectionClass>>();
}
public class CustomSectionClass
{
public int SomeProperty { get; set; }
public string SomeOtherProperty { get; set; }
}
}

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; }
}

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