This really amazes me. I am reading values from appsettings.json using Configuration.GetSection method and in nutshell my appsettings.json looks like below:
"AppSettings":
{
"PathPrefix": "",
"Something": "Something else",
"Clients":{"foo": "bar"}
}
Now I surprises me is that if I do something like below:
var foo = Configuration.GetSection("AppSettings:Clients:foo").Value;
Then it gets the value correctly. It gets the value bar
However, when I do
var clients = Configuration.GetSection("AppSettings:Clients").Value;
it returns null. It's not only this field, whenever I call getSection method to get any complex object then it returns null but when I call it to get a basic string value then it gets the value correctly even though seeminglyi, it had problems in getting its parent element. This baffles me and raises three questions:
Why would it have issues getting complex values but not getting basic string values?
Is it by design? If so , why?
If I want to load entire object, how do I do that?
You can load an entire object using a strongly typed object.
First, create a class (or classes) to hold you settings. Based on your example this would look like:
public class AppSettings
{
public string PathPrefix { get; set; }
public string Something { get; set; }
public Clients Clients { get; set; }
}
public class Clients
{
public string foo { get; set; }
}
Now, you need to add the Options service to your service collection and load your settings from the configuration:
public void ConfigureServices(IServiceCollection services)
{
// This is only required for .NET Core 2.0
services.AddOptions();
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
services.AddMvc();
}
You now access the properties by injecting them into your class, for example:
public class HomeController : Controller
{
private readonly AppSettings _settings;
public HomeController(IOptions<AppSettings> settings)
{
_settings = settings.Value;
}
}
You can also load suboptions in the ConfigureService method by specifying the configuration section to load e.g.
services.Configure<Clients>(Configuration.GetSection("AppSettings:Clients");
Now you can inject IOptions<Clients> to access those settings
The official documentation can be found here
What would you expect it to return? You can get complex objects using the Get<T> extension method. Try this:
var clients = Configuration.GetSection("AppSettings:Clients").Get<YourClientsType>();
Related
I have a few similar classes with similar configurations in it.
So, I want to create a base Class with common configuration, after that inherit it in child config class and in the end initialize it in logic class:
public class OptionsBase
{
public string ConnectionKey { get; set; }
}
public class OptionsChild: OptionsBase
{
public string ChildName{ get; set; }
}
My json file:
"OptionsBase": {
"ConnectionKey": "basekey"
},
"OptionsChild": {
"ChildName" "child1"
}
Expecting to use it like this:
public class Child1
{
private readonly OptionsChild _options;
public Child1(IOptions<OptionsChild> options)
{
_options = options.Value;
}
public void Example()
{
var valueFromParent = _options.ConnectionKey;
}
}
How can I do it without duplication in json file and without using additional common class with common options?
It is web app, config initialization executed like this:
service.Configure<OptionsChild>(config.GetSection("OptionsChild"));
Thank you!
EDIT: I found the solution and added an answer. Please, let me know if the solution is OK.
I have found an answer, here it is described, exactly what I wanted to receive.
It is a bit workaround, need to configure each configuration class twice, but this way I can use base options without duplicating options in child options class and also I don't need to duplicate configuration in JSON file
Please, let me know if the solution is OK.
I have a Blazor component for creating graphs with ChartJs. Based on the models, C# creates a Json for the Chart configuration. Some configurations have defined values that I can use: for example, the PointStyle accepts only few values such as circle and cross.
For this reason, I want to have the constraint to select only the accepted values for this configuration. Then, in my project I created a class PointStyle to have a sort of enum with string.
public class PointStyle
{
private PointStyle(string value) { Value = value; }
public string Value { get; private set; }
public static PointStyle Circle { get { return new PointStyle("circle"); } }
public static PointStyle Cross { get { return new PointStyle("cross"); } }
}
To obtain the correct Json configuration, in the main model I created 2 properties: PointStyle and PointStyleString.
[JsonIgnore]
public PointStyle? PointStyle {
get => _pointStyle;
set
{
_pointStyle = value;
PointStyleString = _pointStyle.Value;
}
}
private PointStyle? _pointStyle;
[JsonPropertyName("pointStyle")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? PointStyleString { get; set; }
PointStyle accepts only the values in the defined list but this is ignored from the Json converter. For example, I can configure this like
PointStyle = PointStyle.Cross
Then, the public variable PointStyleString contains the string I have to serialize in the Json and this is updated when the PointStyle sets a new value.
Now, I have to read the property Value from the class PointStyle that contains the string I have to pass to the configuration. For this reason, I have a private variable _pointStyle that saves the value for PointStyle; so, when a new value is set, I also set the PointStyleString.
When the configuration is ready, I pass through JSRuntime the configuration like
await JSRuntime.InvokeVoidAsync("setup", Config.CanvasId, dotnet_ref, Config);
I don't know exactly how the Config is translated to the Json but the resulted Json is correct for the graph.
Everything I described above is working but it seems to me quite complicated for just setting a value. Do you think I can simplify this code?
I'm just getting my head round setting up my own services to inject from Startup.cs in an ASP.NET Core 3.1+ project.
So far I created an interface and a class:
public interface IMyClass
{
public string SomeString { get; set; }
}
public class MyClass : IMyClass
{
public string SomeString { get; set; }
}
I added the service in startup using:
services.AddScoped<Services.IMyClass, Services.MyClass>();
Which works fine, except I want this class to have various strings it can return based on the current configuration and I have no idea how to pass those variables from appsettings.json to populate SomeString so I can then get it from in a controller.
I was trying this:
services.AddScoped<Services.IMyClass, Services.MyClass>(
Configuration.GetValue<string>("SomeAppSettingKey");
To pull a string from appsettings, but don't know how to populate a constructor in the class with the string. Do I need to add some sort of options class as a property in MyClass ?
There's lots of hints on how I can be adding some of these config settings but I'm just missing some info e.g.
services.AddTransient<IMyClass, MyClass>();
services.Configure<MyClass>(Configuration);
Makes syntactical sense to me but I'm still not sure how to populate the string in MyClass by passing the Configuration here.
Create a model to store the setting extracted from configuration
public class MyClassOptions {
public string SomeAppSettingKey { get; set; }
}
(Note the matching names)
Configure it at Startup
services.Configure<MyClassOptions>(Configuration);
(This assumes the key is in the root of the settings file)
Update the target class to inject the options configured
public class MyClass : IMyClass {
public MyClass (IOptions<MyClassOptions> options) {
SomeString = options.Value.SomeAppSettingKey;
}
public string SomeString { get; set; }
}
Reference Options pattern in ASP.NET Core
I have a appsettings.json that look like this
{
"AppName": "my-app-service",
"MyModel": {
"MyList": [{
"CountryCode": "jp"
},{
"CountryCode": "us"
}]
}
}
Now, i have a POCO file, (MyList.cs is ommited, it has a single string key countryCode)
public class MyModel
{
public IList<MyList> MyList;
}
I wish to make it so that MyModel can be injected anywhere in my code, how do I do that? I see that people do this in their setup.cs
services.Configure<MyModel>(Configuration.GetSection("MyModel"));
How ever it looks like when I use IOption<MyModel> in my code in the contructors, I just get null value. Where am I wrong?
You are correct: calling Configure<T> sets up the options infrastructure for T. This include IOptions<T>, IOptionsMonitor<T> and IOptionsSnapshot<T>. In its simplest forms, configuring the value uses an Action<T> or as in your example binding to a specific IConfiguration. You may also stack multiple calls to either form of Configure. This then allows you to accept an IOptions<T> (or monitor/snapshot) in your class' constructor.
The easiest way to determine if binding to an IConfigurationSection is working as you intend it to is to manually perform the binding and then inspect the value:
var opts = new MyClass();
var config = Configuration.GetSection("MyClass");
// bind manually
config.Bind(opts);
// now inspect opts
The above is dependant on the Microsoft.Extensions.Configuration.Binder package which you should already have as a transitive dependency if you are referencing the Options infrastructure.
Back to your issue: the binder will only bind public properties by default. In your case MyClass.MyList is a field. To get the binding to work you must change it to a property instead.
If you wanted/needed to bind non-public properties you could pass an Action<BinderOptions>:
// manually
var opts = new MyClass();
var config = Configuration.GetSection("MyClass");
// bind manually
config.Bind(opts, o => o.BindNonPublicProperties = true);
// DI/services
var config = Configuration.GetSection("MyClass");
services.Configure<MyClass>(config, o => o.BindNonPublicProperties = true);
Even with BinderOptions there is still no way to bind to fields. Also note that there is varying behavior for things like collection interfaces, arrays and get only properties. You may need to play around a bit to ensure things are binding as you intend.
If you have some appsettings.json like :
{
"SomeConfig": {
"Key1": "Value1",
"Key2": "Value2",
"Key3": "Value3"
}
}
Then you can have your POCO as :
public struct SomeConfig
{
public string Key1 { get; set; }
public string Key2 { get; set; }
public string Key3 { get; set; }
}
After this you need to put
services.Configure<SomeConfig>(Configuration.GetSection("SomeConfig")); entry
in public void ConfigureServices(IServiceCollection services)
Now in any class where you want to use it :
private readonly ILogger logger;
private readonly SomeConfig someConfigurations;
public SampleService(IOptions<SomeConfig> someConfigOptions, ILogger logger)
{
this.logger = logger;
someConfigurations = someConfigOptions.Value;
logger.Information($"Value of key1 : '{someConfigurations.Key1}'");
}
Recently we learned about AppDomain Recycling of IIS and how it affects static variables setting them to their primary values (nulls, 0s, etc).
We use some static variables that are initialized in a static constructor (for first time initialization, configuration values like "number of decimal places", "administrator email", etc... that are retrieved from DB) and then only read their value along the website execution.
Whats the best way of solving this problem? Some possible ideas:
Checking if variable is null/0 at each retrieval (don't like it because of a possible performance impact + time spent to add this check to each variable + code overload added to the project)
Somehow preventing AppDomain Recycling (this reset logic doesn't happen in Windows forms with static variables, shouldn't it work similarly as being the same language in both environments? At least in terms of standards as static variables management)
Using some other way of holding these variables (but we think that for being some values used for info as global reference for all users, static variables were the best option performance/coding wise)
Subscribing to an event that is triggered in those AppDomain Recycling so we can reinitialize all those variables (maybe best option if recycling can't be prevented...)
Ideas?
I would go with the approach that you don't like.
Checking if variable is null/0 at each retrieval (don't like it because of a possible performance impact + time spent to add this check to each variable + code overload added to the project)
I think it's faster than retireving from web.config.
You get a typed object
Its not a performance impact as you are not going to database on every retrieval request. You'll go to database (or any source) only when you find that current value set to its default value.
Checking the null wrapped into code:
public interface IMyConfig {
string Var1 { get; }
string Var2 { get; }
}
public class MyConfig : IMyConfig {
private string _Var1;
private string _Var2;
public string Var1 { get { return _Var1; } }
public string Var2 { get { return _Var2; } }
private static object s_SyncRoot = new object();
private static IMyConfig s_Instance;
private MyConfig() {
// load _Var1, _Var2 variables from db here
}
public static IMyConfig Instance {
get {
if (s_Instance != null) {
return s_Instance;
}
lock (s_SyncRoot) {
s_Instance = new MyConfig();
}
return s_Instance;
}
}
}
Is there any reason why you can't store these values in your web.config file and use ConfiguationManager.AppSettings to retrieve them?
ConfigurationManager.AppSettings["MySetting"] ?? "defaultvalue";
In view of your edit, why not cache the required values when they're first retrieved?
var val = HttpContext.Cache["MySetting"];
if (val == null)
{
val = // Database retrieval logic
HttpContext.Cache["MySetting"] = val;
}
It sounds like you need a write-through (or write-behind) cache, which can be done with static variables.
Whenever a user changes the value, write it back to the database. Then, whenever the AppPool is recycled (which is a normal occurrence and shouldn't be avoided), the static constructors can read the current values from the database.
One thing you'll have to consider: If you ever scale out to a web farm, you'll need to have some sort of "trigger" when a shared variable changes so the other servers on the farm can know to retrieve the new values from the server.
Comments on other parts of your question:
(don't like [Checking if variable is null/0 at each retrieval] because of a possible performance impact + time spent to add this check to each variable + code overload added to the project
If you use a write-through cache you won't need this, but in either case The time spent to check a static variable for 0 or null should be negligible.
[AppDomain recycling] doesn't happen in Windows forms with static variables, shouldn't it work similarly as being the same language in both environments?
No, WebForms and WinForms are completely different platforms with different operating models. Web sites should be able to respond to many (up to millions) of concurrent users. WinForms are built for single-user access.
've resolved this kind of issue, following a pattern similar to this. This enabled me to cater for handling circumstances where the data could change. I set up my ISiteSettingRepository in the bootstrapper. In 1 application I get the configuration from an XML file but in others I get it from the database, as and when I need it.
public class ApplicationSettings
{
public ApplicationSettings()
{
}
public ApplicationSettings(ApplicationSettings settings)
{
ApplicationName = settings.ApplicationName;
EncryptionAlgorithm = settings.EncryptionAlgorithm;
EncryptionKey = settings.EncryptionKey;
HashAlgorithm = settings.HashAlgorithm;
HashKey = settings.HashKey;
Duration = settings.Duration;
BaseUrl = settings.BaseUrl;
Id = settings.Id;
}
public string ApplicationName { get; set; }
public string EncryptionAlgorithm { get; set; }
public string EncryptionKey { get; set; }
public string HashAlgorithm { get; set; }
public string HashKey { get; set; }
public int Duration { get; set; }
public string BaseUrl { get; set; }
public Guid Id { get; set; }
}
Then a "Service" Interface to
public interface IApplicaitonSettingsService
{
ApplicationSettings Get();
}
public class ApplicationSettingsService : IApplicaitonSettingsService
{
private readonly ISiteSettingRepository _repository;
public ApplicationSettingsService(ISiteSettingRepository repository)
{
_repository = repository;
}
public ApplicationSettings Get()
{
SiteSetting setting = _repository.GetAll();
return setting;
}
}
I would take a totally different approach, one that doesn't involve anything static.
First create a class to strongly-type the configuration settings you're after:
public class MyConfig
{
int DecimalPlaces { get; set; }
string AdministratorEmail { get; set; }
//...
}
Then abstract away the persistence layer by creating some repository:
public interface IMyConfigRepository
{
MyConfig Load();
void Save(MyConfig settings);
}
The classes that can read and write these settings can then statically declare that they depend on an implementation of this repository:
public class SomeClass
{
private readonly IMyConfigRepository _repo;
public MyClass(IMyConfigRepository repo)
{
_repo = repo;
}
public void DoSomethingThatNeedsTheConfigSettings()
{
var settings = _repo.Load();
//...
}
}
Now implement the repository interface the way you want (today you want the settings in a database, tomorrow might be serializing to a .xml file, and next year using a cloud service) and the config interface as you need it.
And you're set: all you need now is a way to bind the interface to its implementation. Here's a Ninject example (written in a NinjectModule-derived class' Load method override):
Bind<IMyConfigRepository>().To<MyConfigSqlRepository>();
Then, you can just swap the implementation for a MyConfigCloudRepository or a MyConfigXmlRepository implementation when/if you ever need one.
Being an asp.net application, just make sure you wire up those dependencies in your Global.asax file (at app start-up), and then any class that has a IMyConfigRepository constructor parameter will be injected with a MyConfigSqlRepository which will give you MyConfigImplementation objects that you can load and save as you please.
If you're not using an IoC container, then you would just new up the MyConfigSqlRepository at app start-up, and manually inject the instance into the constructors of the types that need it.
The only thing with this approach, is that if you don't already have a DependencyInjection-friendly app structure, it might mean extensive refactoring - to decouple objects and eliminate the newing up of dependencies, making unit tests much easier to get focused on a single aspect, and much easier to mock-up the dependencies... among other advantages.