-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for repeated XML elements without a name attribute #44608
Conversation
This commit adds support in Microsoft.Extensions.Configuration.Xml for repeated XML elements without requiring a Name attribute. This solves a particularly subtle bug when configuring Serilog from an XML configuration source. For a full description, see dotnet#36541 The original implementation of the XmlStreamConfigurationProvider has been modified to do the following: - Maintain a stack of encountered XML elements while traversing the XML source. This is needed to detect siblings. - When siblings are detected, automatically append an index to the generated configuration keys. This makes it work exactly the same as the JSON configuration provider with JSON arrays. Tests are updated to reflect the new behavior: - Tests that verified an exception occurs when entering duplicate keys have been removed. Duplicate keys are supported now. - Add tests that verify duplicate keys result in the correct configuration keys, with all the lower/upper case variants and with support for the special "Name" attribute handling.
Tagging subscribers to this area: @maryamariyan Issue meta data
|
Configuration tests failing shown in https://dev.azure.com/dnceng/public/_build/results?buildId=884531&view=ms.vss-test-web.build-test-results-tab |
…ible with new implementation
…is is no longer required
@maryamariyan CI is green now. The tests that verify the configuration with duplicate keys in them are not compatible with the way the repeated elements work in XML configuration. To be more specific, the 'DuplicatesTestConfig' provides the following test data: public static TestSection DuplicatesTestConfig { get; }
= new TestSection
{
Values = new[]
{
("Key1", (TestKeyValue)"Value1"),
("Key1", (TestKeyValue)"Value1")
},
...
}; Later, this is verified: protected virtual void AssertConfig(
IConfigurationRoot config,
bool expectNulls = false,
string nullValue = null)
{
var value1 = expectNulls ? nullValue : "Value1";
...
Assert.Equal(value1, config["Key1"], StringComparer.InvariantCultureIgnoreCase);
...
} But this fails, because "Key1" is not in the output. This is because the XML config provider will produce two entries, "Key1:0" and "Key1:1", both with value "Value1". |
Any update on this? |
We'll be reviewing this week, |
src/libraries/Microsoft.Extensions.Configuration.Xml/tests/XmlConfigurationTest.cs
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration.Xml/src/IXmlConfigurationValue.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElement.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElementContent.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration.Xml/tests/ConfigurationProviderXmlTest.cs
Show resolved
Hide resolved
The XML configuration provider now builds a simple in-memory model of the XML elements that it encounters Each element keeps track of its attributes, children and content. Furthermore, each element also has a reference to its siblings. Each group of siblings all share the same list. All of the above makes it possible to intelligently produce configuration keys and values, taking into account repeated XML elements.
@maryamariyan I took a look at your comments, and almost all of them stem from the same issue: the "Key" property was lazily computed in various places, because you cannot compute it once in the constructor. This is because the value of "IsMultiple" might change, because a new element is encountered during traversal that shares the same name. This triggers the new behavior where the "sibling index" is then added to the key. To address your concerns, I slightly reworked the implementation, and I would be interested to hear your thoughts. I've removed all "Key" and "Value" computation logic from the new classes, they are now extremely simple POCOs. Instead, all of the heavy lifting now happens in The "algorithm" now works as follows:
|
… works as expected
@maryamariyan Any chance this PR could get another review? |
Thanks for the update, I'll do a comprehensive review within next week, |
Hey @amoerie, I haven't forgotten about this one, I'll make sure this keeps moving and totally appreciate how you kept the fix up to date. |
Thanks! On the bright side, I guess I can say I've been contributing to Microsoft Open Source for 3 years already. 😉 |
src/libraries/Microsoft.Extensions.Configuration.Xml/tests/ConfigurationProviderXmlTest.cs
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElement.cs
Outdated
Show resolved
Hide resolved
@amoerie thanks for running the benchmarks. I don't think we have any benchmarks for Microsoft.Extensions.Configuration yet: https://github.com/dotnet/performance/tree/master/src/benchmarks/micro/libraries based on the results and your investigation, I think taking a 5-20% regression is not acceptable, specially since we could try and improve your current implementation with the info you got from the profiling.
Is there a way to get the line info only if an exception is thrown? Removing it from the error might be breaking and I think having it in the error is valuable as it helps you know where the error in your configuration file is. |
@safern I've pushed some optimizations, if you would find the time again to review that.
Avoiding the line info reading would require completely revising the algorithm, which I'm hesitant to do right now. But I found that the most time was spent on This is a summary of the improvements I've made:
All of this amounts to an implementation that is even slightly faster than the original one! 🎉 These are the performance results of these modifications: BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
Intel Core i7-8700K CPU 3.70GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=5.0.103
[Host] : .NET Core 5.0.3 (CoreCLR 5.0.321.7212, CoreFX 5.0.321.7212), X64 RyuJIT
DefaultJob : .NET Core 5.0.3 (CoreCLR 5.0.321.7212, CoreFX 5.0.321.7212), X64 RyuJIT
Benchmarks with issues: |
src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlStreamConfigurationProvider.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlStreamConfigurationProvider.cs
Show resolved
Hide resolved
Thanks, @amoerie. I'm happy that you were able to find a way that it even improves the perf. Now that you wrote some benchmarks, can we add them to: https://github.com/dotnet/performance/tree/master/src/benchmarks/micro/libraries ? That way our infra will catch if there are any regressions in the future. I left a couple of comments, but overall looks good. |
This avoids confusion with the ProcessXYZ helper methods that run during a different stage in the algorithm
Thanks, I'll see if I can add the benchmarks there. |
Timeout is: #49309 |
@amoerie thanks for the effort to get this fix in 🎉 |
You're very welcome! It's been quite the experience, I learned quite a few things during this endeavor. 💪 Thank you (and @maryamariyan too) for taking time out of your schedules to review and discuss the work of a total stranger. From my own work experience, I know this is not trivial. For me personally, I consider it a very big deal to have contributed to .NET itself. So thanks for making that possible. 👍 |
Anytime! Feel free to browse for more up-for-grabs issues and help us make .NET better and reach out for any help if needed, we really appreciate and like the community help! |
I have created dotnet/docs#30660 to document this breaking change. |
New PR because #43722 was out of date and CI was broken at the time. See that PR for initial review.
Fixes #36541
Fixes #36561
This pull request adds support in Microsoft.Extensions.Configuration.Xml for repeated XML elements without requiring a Name attribute.
This solves a particularly subtle bug when configuring Serilog from an XML configuration source. For a full description, see #36541
The original implementation of the XmlStreamConfigurationProvider has been modified to do the following:
Maintain a stack of encountered XML elements while traversing the XML source. This is needed to detect siblings.
When siblings are detected, automatically append an index to the generated configuration keys. This makes it work exactly the same as the JSON configuration provider with JSON arrays.
Tests are updated to reflect the new behavior:
Tests that verified an exception occurs when entering duplicate keys have been removed. Duplicate keys are supported now.
Add tests that verify duplicate keys result in the correct configuration keys, with all the lower/upper case variants and with support for the special "Name" attribute handling.
Thank you for your consideration.