Reputation: 21011
I'm using ASP.NET Core 2.1. I have settings in appsettings.json
and I bind them to classes using the options pattern. I want to override some of them in appsettings.Production.json
.
Overriding is supported according to the docs, and works for me generally. But it doesn't work for arrays.
appsettings.json
:
"MySectionOuter": {
"MySectionInner": [
{
"foo": "1",
"bar": "2",
"baz": "3"
},
{
"foo": "a",
"bar": "b",
"baz": "c"
}
]
}
My overrides in appsettings.Production.json
"MySectionOuter": {
"MySectionInner": [
{
"bar": "4",
},
{
"baz": "d"
}
]
}
However that doesn't work - it adds rather than replaces.
I read that the array is syntactic sugar for a key-value store. So I also tried this:
"MySectionOuter": {
"MySection:1": {
"bar": "4",
},
"MySection:2": {
"baz": "b",
}
}
But that also doesn't work.
What is the correct syntax?
UPDATE
The comments show I haven't explained properly. What I want is like this:
During development:
element1: foo=1
element1: bar=2
element1: baz=3
element2: foo=a
element2: bar=b
element2: baz=c
During production:
element1: foo=1
element1: bar=2
element1: baz=4 // this was changed
element2: foo=a
element2: bar=b
element2: baz=d // this was changed
Upvotes: 51
Views: 27650
Reputation: 376
I was actually having a similar issue in dotnet 6, when trying to merge arrays from multiple appsettings, when I stumbled across this thread.
The solution was actually way simpler then thought.
Microsoft.Extensions.Configuration merges arrays through the index:
{ foo: [1, 2, 3] } + { foo: [4, 5] } = { foo: 4, 5, 3 }
But we want to be able to declare which entries override others and which ones should be added to the list. And we do this by declaring a GUID as dictionary key instead of an array.
{
"foo": {
"870622cb-0372-49f3-a46e-07a1bd0db769": 1,
"cbb3af55-94ea-41a5-bbb5-cb936ac47249": 2,
"9410fcdc-28b3-4bff-bfed-4d7286b33294": 3
}
}
+
{
"foo": {
"cbb3af55-94ea-41a5-bbb5-cb936ac47249": 4,
"1c43fa78-b8db-41f8-809d-759a4bc35ee2": 5,
}
}
=
{
"foo": {
"870622cb-0372-49f3-a46e-07a1bd0db769": 1,
"cbb3af55-94ea-41a5-bbb5-cb936ac47249": 4, /*2 changed to 4 because key equals*/
"9410fcdc-28b3-4bff-bfed-4d7286b33294": 3
"1c43fa78-b8db-41f8-809d-759a4bc35ee2": 5, /*while 5 added to the list*/
}
}
This may seem inconventient at first, because one would think, that
((IConfiguration)config).GetSection("foo").Get<int[]>();
Throws some kind of invalid type exception, since a Guid is not what we know as array index.
But it can actually (implicitly!) return the merged "foo" section above as int[]
.
Upvotes: 7
Reputation: 3367
In fact, there are no arrays there when the configuration is built. It's just a key-value pair dictionary. So you end up with string keys, something like
"mysectionouter:mysectioninner:0:foo" = 1
.
So when in your config you define an array, the following happens:
appsettings.json:
"mysectionouter:mysectioninner:0:foo" = 1
"mysectionouter:mysectioninner:0:bar" = 2
appsettings.production.json:
"mysectionouter:mysectioninner:0:bar" = new1
result:
foo = 1
bar = new1
So it's just index-based, and next configuration just overrides a key. In your second example, you achieve nothing but changing the index. Representation would be:
"mysectionouter:mysectioninner:1:bar" = new1
So back to your question: arrays are tricky in appsettings, and though supported, are generally hard and not intuitive to use.
By index you may get a weird merge of two not related objects, if you define different sets of settings in your files, like settings A and B in the first config, and C in second, you will get C and B in the result, and you likely don't want to have B at all. Worse still, you can get a mix of A and C if you define only some fields of each object.
I'd recommend using some other files for storing this kind of information. You can also break in the debugger just where the configuration is loaded and see for yourself how these keys are build to get more insight.
Upvotes: 59
Reputation: 2836
According to this blog post: https://www.paraesthesia.com/archive/2018/06/20/microsoft-extensions-configuration-deep-dive/
It's not possible to remove configuration items with a provider.
You can add configuration at override time, but you can’t remove things. The best you can do is override a value with an empty string.
Instead you should only fill as little information as needed in the appsettings.config
and fill the appropriate settings in a more specialized settings file. E.g. appsettings.Development.config
or your appsettings.Production.config
. Or as suggested in the blog post:
Since you can’t remove things, specify as little configuration as possible and behave correctly using defaults in your code when configuration isn’t there.
Upvotes: 23