You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When WithSubstitution is called multiple times on the same IConfigurationBuilder, it causes recursive accumulation of configuration sources, leading to excessive memory usage.
Root Cause
In SubstitutingConfigurationSource.Build(), when building valueRoot, the code iterates through ALL sources in builder.Sources except itself and creates a new ConfigurationBuilder with copies of those sources. Each subsequent call to WithSubstitution adds a new SubstitutingConfigurationSource that, when built, will copy all previously added sources again.
Demo
To see allocated memory depending on the number of added WithSubstitution calls, you can try:
Also, I would suggest to add configurable option to prevent multiple registrations:
publicclassSubstitutingSettings{/// <summary>/// Use single substituting source to prevent multiple registrations, which can cause memory leaks./// Default value: false/// </summary>publicboolSingleSubstitutingSource{get;set;}=false;}
internalclassSubstitutingConfigurationSource:IConfigurationSource{privatereadonlySubstitutingSettings_settings;publicSubstitutingConfigurationSource(...,SubstitutingSettingssettings){_settings=settings;}publicIConfigurationProviderBuild(IConfigurationBuilderbuilder){// ...varvalueBuilder=newConfigurationBuilder();for(vari=0;i<builder.Sources.Count;i++){varsource=builder.Sources[i];if(ReferenceEquals(source,this)){continue;}// Check if SingleSubstitutingSource is enabledif(_settings.SingleSubstitutingSource&&sourceisSubstitutingConfigurationSource){thrownewInvalidOperationException("Multiple substitution sources were detected when SingleSubstitutingSource setting is enabled.");}valueBuilder.Add(source);}// ...}}
Usage:
varconfiguration=newConfigurationBuilder().WithSubstitution(
c => ...,newSubstitutingSettings{SingleSubstitutingSource=true}).WithSubstitution(
c => ...,newSubstitutingSettings{SingleSubstitutingSource=true}).Build();
SingleSubstitutingSource could be set to true by default to prevent such memory leaks, but this might break existing behavior. A safer approach is to keep the default as false and recommend enabling it in new projects, or introduce this change in a major version update.
Discussion
What do you think? Are there more elegant solutions – for example, adding a CycleDetector at build time to prevent recursive accumulation?
I'm open to suggestions. Would a pull request be welcome?
Description
When
WithSubstitutionis called multiple times on the sameIConfigurationBuilder, it causes recursive accumulation of configuration sources, leading to excessive memory usage.Root Cause
In
SubstitutingConfigurationSource.Build(), when buildingvalueRoot, the code iterates through ALL sources inbuilder.Sourcesexcept itself and creates a newConfigurationBuilderwith copies of those sources. Each subsequent call toWithSubstitutionadds a newSubstitutingConfigurationSourcethat, when built, will copy all previously added sources again.Demo
To see allocated memory depending on the number of added
WithSubstitutioncalls, you can try:On my machine, the results were:
In real projects, there could be many sources, and this behavior may cause memory problems.
Suggestions
WithSubstitutionif possible.Important
Avoid the following pattern to prevent memory leaks:
Use a single
WithSubstitutionwith multiple sources instead:Also, I would suggest to add configurable option to prevent multiple registrations:
Usage:
SingleSubstitutingSourcecould be set totrueby default to prevent such memory leaks, but this might break existing behavior. A safer approach is to keep the default asfalseand recommend enabling it in new projects, or introduce this change in a major version update.Discussion
What do you think? Are there more elegant solutions – for example, adding a
CycleDetectorat build time to prevent recursive accumulation?I'm open to suggestions. Would a pull request be welcome?