From 4ab202cf8ef2cc7c50fa528afb36dcd7b041bf86 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Thu, 8 Jul 2021 07:56:08 -0700 Subject: [PATCH 1/6] Add Config : IConfigurationRoot, IConfigurationBuilder --- .../ref/Microsoft.Extensions.Configuration.cs | 15 + .../src/Config.cs | 376 ++++++ .../tests/ConfigTests.cs | 1202 +++++++++++++++++ 3 files changed, 1593 insertions(+) create mode 100644 src/libraries/Microsoft.Extensions.Configuration/src/Config.cs create mode 100644 src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs diff --git a/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs b/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs index c3cc43f7eff56..110c5b61787c0 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs @@ -28,6 +28,21 @@ public partial class ChainedConfigurationSource : Microsoft.Extensions.Configura public bool ShouldDisposeConfiguration { get { throw null; } set { } } public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; } } + public sealed partial class Config : Microsoft.Extensions.Configuration.IConfigurationBuilder, Microsoft.Extensions.Configuration.IConfigurationRoot, System.IDisposable + { + public Config() { } + public string this[string key] { get { throw null; } set { throw null; } } + public IConfigurationSection GetSection(string key) { throw null; } + public System.Collections.Generic.IEnumerable GetChildren() { throw null; } + public void Dispose() { throw null; } + System.Collections.Generic.IDictionary IConfigurationBuilder.Properties { get { throw null; } } + System.Collections.Generic.IList IConfigurationBuilder.Sources { get { throw null; } } + Microsoft.Extensions.Configuration.IConfigurationBuilder IConfigurationBuilder.Add(Microsoft.Extensions.Configuration.IConfigurationSource source) { throw null; } + Microsoft.Extensions.Configuration.IConfigurationRoot IConfigurationBuilder.Build() { throw null; } + System.Collections.Generic.IEnumerable IConfigurationRoot.Providers { get { throw null; } } + void IConfigurationRoot.Reload() { throw null; } + Primitives.IChangeToken IConfiguration.GetReloadToken() { throw null; } + } public partial class ConfigurationBuilder : Microsoft.Extensions.Configuration.IConfigurationBuilder { public ConfigurationBuilder() { } diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/Config.cs b/src/libraries/Microsoft.Extensions.Configuration/src/Config.cs new file mode 100644 index 0000000000000..4f48304ae7f81 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Configuration/src/Config.cs @@ -0,0 +1,376 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.Extensions.Configuration +{ + /// + /// Configuration is mutable configuration object. It is both an and an . + /// As sources are added, it updates its current view of configuration. Once Build is called, configuration is frozen. + /// + public sealed class Config : IConfigurationBuilder, IConfigurationRoot, IDisposable + { + private readonly ConfigurationSources _sources; + private readonly ConfigurationBuilderProperties _properties; + + private readonly object _providerLock = new(); + private readonly List _providers = new(); + private readonly List _changeTokenRegistrations = new(); + private ConfigurationReloadToken _changeToken = new(); + + /// + /// Creates an empty mutable configuration object that is both an and an . + /// + public Config() + { + _sources = new ConfigurationSources(this); + _properties = new ConfigurationBuilderProperties(this); + + // Make sure there's some default storage since there are no default providers. + this.AddInMemoryCollection(); + + AddSource(_sources[0]); + } + + /// + public string this[string key] + { + get + { + lock (_providerLock) + { + for (int i = _providers.Count - 1; i >= 0; i--) + { + var provider = _providers[i]; + + if (provider.TryGet(key, out string value)) + { + return value; + } + } + + return null; + } + } + set + { + lock (_providerLock) + { + if (_providers.Count == 0) + { + throw new InvalidOperationException("A configuration source is not registered. Please register one before setting a value."); + } + + foreach (var provider in _providers) + { + provider.Set(key, value); + } + } + } + } + + /// + public IConfigurationSection GetSection(string key) => new ConfigurationSection(this, key); + + /// + public IEnumerable GetChildren() + { + lock (_providerLock) + { + // ToList() to eagerly evaluate inside lock. + return _providers + .Aggregate(Enumerable.Empty(), + static (seed, source) => source.GetChildKeys(seed, parentPath: null)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .Select(GetSection) + .ToList(); + } + } + + IDictionary IConfigurationBuilder.Properties => _properties; + + IList IConfigurationBuilder.Sources => _sources; + + IEnumerable IConfigurationRoot.Providers + { + get + { + lock (_providerLock) + { + return new List(_providers); + } + } + } + + /// + public void Dispose() + { + lock (_providerLock) + { + DisposeRegistrationsAndProvidersUnsynchronized(); + } + } + + IConfigurationBuilder IConfigurationBuilder.Add(IConfigurationSource source) + { + _sources.Add(source ?? throw new ArgumentNullException(nameof(source))); + return this; + } + + IConfigurationRoot IConfigurationBuilder.Build() => this; + + IChangeToken IConfiguration.GetReloadToken() => _changeToken; + + void IConfigurationRoot.Reload() + { + lock (_providerLock) + { + foreach (var provider in _providers) + { + provider.Load(); + } + } + + RaiseChanged(); + } + + private void RaiseChanged() + { + var previousToken = Interlocked.Exchange(ref _changeToken, new ConfigurationReloadToken()); + previousToken.OnReload(); + } + + // Don't rebuild and reload all providers in the common case when a source is simply added to the IList. + private void AddSource(IConfigurationSource source) + { + lock (_providerLock) + { + var provider = source.Build(this); + _providers.Add(provider); + + provider.Load(); + _changeTokenRegistrations.Add(ChangeToken.OnChange(() => provider.GetReloadToken(), () => RaiseChanged())); + } + + RaiseChanged(); + } + + // Something other than Add was called on IConfigurationBuilder.Sources or IConfigurationBuilder.Properties has changed. + private void ReloadSources() + { + lock (_providerLock) + { + DisposeRegistrationsAndProvidersUnsynchronized(); + + _changeTokenRegistrations.Clear(); + _providers.Clear(); + + foreach (var source in _sources) + { + _providers.Add(source.Build(this)); + } + + foreach (var p in _providers) + { + p.Load(); + _changeTokenRegistrations.Add(ChangeToken.OnChange(() => p.GetReloadToken(), () => RaiseChanged())); + } + } + + RaiseChanged(); + } + + private void DisposeRegistrationsAndProvidersUnsynchronized() + { + // dispose change token registrations + foreach (var registration in _changeTokenRegistrations) + { + registration.Dispose(); + } + + // dispose providers + foreach (var provider in _providers) + { + (provider as IDisposable)?.Dispose(); + } + } + + private class ConfigurationSources : IList + { + private readonly List _sources = new(); + private readonly Config _config; + + public ConfigurationSources(Config config) + { + _config = config; + } + + public IConfigurationSource this[int index] + { + get => _sources[index]; + set + { + _sources[index] = value; + _config.ReloadSources(); + } + } + + public int Count => _sources.Count; + + public bool IsReadOnly => false; + + public void Add(IConfigurationSource source) + { + _sources.Add(source); + _config.AddSource(source); + } + + public void Clear() + { + _sources.Clear(); + _config.ReloadSources(); + } + + public bool Contains(IConfigurationSource source) + { + return _sources.Contains(source); + } + + public void CopyTo(IConfigurationSource[] array, int arrayIndex) + { + _sources.CopyTo(array, arrayIndex); + } + + public IEnumerator GetEnumerator() + { + return _sources.GetEnumerator(); + } + + public int IndexOf(IConfigurationSource source) + { + return _sources.IndexOf(source); + } + + public void Insert(int index, IConfigurationSource source) + { + _sources.Insert(index, source); + _config.ReloadSources(); + } + + public bool Remove(IConfigurationSource source) + { + var removed = _sources.Remove(source); + _config.ReloadSources(); + return removed; + } + + public void RemoveAt(int index) + { + _sources.RemoveAt(index); + _config.ReloadSources(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + private class ConfigurationBuilderProperties : IDictionary + { + private readonly Dictionary _properties = new(); + private readonly Config _config; + + public ConfigurationBuilderProperties(Config config) + { + _config = config; + } + + public object this[string key] + { + get => _properties[key]; + set + { + _properties[key] = value; + _config.ReloadSources(); + } + } + + public ICollection Keys => _properties.Keys; + + public ICollection Values => _properties.Values; + + public int Count => _properties.Count; + + public bool IsReadOnly => false; + + public void Add(string key, object value) + { + _properties.Add(key, value); + _config.ReloadSources(); + } + + public void Add(KeyValuePair item) + { + ((IDictionary)_properties).Add(item); + _config.ReloadSources(); + } + + public void Clear() + { + _properties.Clear(); + _config.ReloadSources(); + } + + public bool Contains(KeyValuePair item) + { + return _properties.Contains(item); + } + + public bool ContainsKey(string key) + { + return _properties.ContainsKey(key); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((IDictionary)_properties).CopyTo(array, arrayIndex); + } + + public IEnumerator> GetEnumerator() + { + return _properties.GetEnumerator(); + } + + public bool Remove(string key) + { + var wasRemoved = _properties.Remove(key); + _config.ReloadSources(); + return wasRemoved; + } + + public bool Remove(KeyValuePair item) + { + var wasRemoved = ((IDictionary)_properties).Remove(item); + _config.ReloadSources(); + return wasRemoved; + } + + public bool TryGetValue(string key, out object value) + { + return _properties.TryGetValue(key, out value); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _properties.GetEnumerator(); + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs b/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs new file mode 100644 index 0000000000000..7a39c8fa9188d --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs @@ -0,0 +1,1202 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration.Memory; +using Microsoft.Extensions.Primitives; +using Moq; +using Xunit; + +namespace Microsoft.Extensions.Configuration.Test +{ + public class ConfigTests + { + [Fact] + public void AutoUpdates() + { + var config = new Config(); + + config.AddInMemoryCollection(new Dictionary + { + { "TestKey", "TestValue" }, + }); + + Assert.Equal("TestValue", config["TestKey"]); + } + + [Fact] + public void TriggersReloadTokenOnSourceAddition() + { + var config = new Config(); + + var reloadToken = ((IConfiguration)config).GetReloadToken(); + + Assert.False(reloadToken.HasChanged); + + config.AddInMemoryCollection(new Dictionary + { + { "TestKey", "TestValue" }, + }); + + Assert.True(reloadToken.HasChanged); + } + + + [Fact] + public void SettingValuesWorksWithoutManuallyAddingSource() + { + var config = new Config + { + ["TestKey"] = "TestValue", + }; + + Assert.Equal("TestValue", config["TestKey"]); + } + + [Fact] + public void SettingConfigValuesDoesNotTriggerReloadToken() + { + var config = new Config(); + var reloadToken = ((IConfiguration)config).GetReloadToken(); + + config["TestKey"] = "TestValue"; + + Assert.Equal("TestValue", config["TestKey"]); + + // ConfigurationRoot doesn't fire the token today when the setter is called. Maybe we should change that. + Assert.False(reloadToken.HasChanged); + } + + [Fact] + public void SettingIConfigurationBuilderPropertiesReloadsSources() + { + var config = new Config(); + IConfigurationBuilder configBuilder = config; + + config["PreReloadTestConfigKey"] = "PreReloadTestConfigValue"; + + var reloadToken1 = ((IConfiguration)config).GetReloadToken(); + // Changing Properties causes all the IConfigurationSources to be reload. + configBuilder.Properties["TestPropertyKey"] = "TestPropertyValue"; + + var reloadToken2 = ((IConfiguration)config).GetReloadToken(); + config["PostReloadTestConfigKey"] = "PostReloadTestConfigValue"; + + Assert.Equal("TestPropertyValue", configBuilder.Properties["TestPropertyKey"]); + Assert.Null(config["TestPropertyKey"]); + + // Changes before the reload are lost by the MemoryConfigurationSource. + Assert.Null(config["PreReloadTestConfigKey"]); + Assert.Equal("PostReloadTestConfigValue", config["PostReloadTestConfigKey"]); + + Assert.True(reloadToken1.HasChanged); + Assert.False(reloadToken2.HasChanged); + } + + [Fact] + public void DisposesProvidersOnDispose() + { + var provider1 = new TestConfigurationProvider("foo", "foo-value"); + var provider2 = new DisposableTestConfigurationProvider("bar", "bar-value"); + var provider3 = new TestConfigurationProvider("baz", "baz-value"); + var provider4 = new DisposableTestConfigurationProvider("qux", "qux-value"); + var provider5 = new DisposableTestConfigurationProvider("quux", "quux-value"); + + var config = new Config(); + IConfigurationBuilder builder = config; + + builder.Add(new TestConfigurationSource(provider1)); + builder.Add(new TestConfigurationSource(provider2)); + builder.Add(new TestConfigurationSource(provider3)); + builder.Add(new TestConfigurationSource(provider4)); + builder.Add(new TestConfigurationSource(provider5)); + + Assert.Equal("foo-value", config["foo"]); + Assert.Equal("bar-value", config["bar"]); + Assert.Equal("baz-value", config["baz"]); + Assert.Equal("qux-value", config["qux"]); + Assert.Equal("quux-value", config["quux"]); + + config.Dispose(); + + Assert.True(provider2.IsDisposed); + Assert.True(provider4.IsDisposed); + Assert.True(provider5.IsDisposed); + } + + [Fact] + public void DisposesProvidersOnRemoval() + { + var provider1 = new TestConfigurationProvider("foo", "foo-value"); + var provider2 = new DisposableTestConfigurationProvider("bar", "bar-value"); + var provider3 = new TestConfigurationProvider("baz", "baz-value"); + var provider4 = new DisposableTestConfigurationProvider("qux", "qux-value"); + var provider5 = new DisposableTestConfigurationProvider("quux", "quux-value"); + + var source1 = new TestConfigurationSource(provider1); + var source2 = new TestConfigurationSource(provider2); + var source3 = new TestConfigurationSource(provider3); + var source4 = new TestConfigurationSource(provider4); + var source5 = new TestConfigurationSource(provider5); + + var config = new Config(); + IConfigurationBuilder builder = config; + + builder.Add(source1); + builder.Add(source2); + builder.Add(source3); + builder.Add(source4); + builder.Add(source5); + + Assert.Equal("foo-value", config["foo"]); + Assert.Equal("bar-value", config["bar"]); + Assert.Equal("baz-value", config["baz"]); + Assert.Equal("qux-value", config["qux"]); + Assert.Equal("quux-value", config["quux"]); + + builder.Sources.Remove(source2); + builder.Sources.Remove(source4); + + // While only provider2 and provider4 need to be disposed here, we do not assert provider5 is not disposed + // because even though it's unnecessary, Configuration disposes all providers on removal and rebuilds + // all the sources. While not optimal, this should be a pretty rare scenario. + Assert.True(provider2.IsDisposed); + Assert.True(provider4.IsDisposed); + + config.Dispose(); + + Assert.True(provider2.IsDisposed); + Assert.True(provider4.IsDisposed); + Assert.True(provider5.IsDisposed); + } + + [Fact] + public void DisposesChangeTokenRegistrationsOnDispose() + { + var changeToken = new TestChangeToken(); + var providerMock = new Mock(); + providerMock.Setup(p => p.GetReloadToken()).Returns(changeToken); + + var config = new Config(); + + ((IConfigurationBuilder)config).Add(new TestConfigurationSource(providerMock.Object)); + + Assert.NotEmpty(changeToken.Callbacks); + + config.Dispose(); + + Assert.Empty(changeToken.Callbacks); + } + + [Fact] + public void DisposesChangeTokenRegistrationsOnRemoval() + { + var changeToken = new TestChangeToken(); + var providerMock = new Mock(); + providerMock.Setup(p => p.GetReloadToken()).Returns(changeToken); + + var source = new TestConfigurationSource(providerMock.Object); + + var config = new Config(); + IConfigurationBuilder builder = config; + + builder.Add(source); + + Assert.NotEmpty(changeToken.Callbacks); + + builder.Sources.Remove(source); + + Assert.Empty(changeToken.Callbacks); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ChainedConfigurationIsDisposedOnDispose(bool shouldDispose) + { + var provider = new DisposableTestConfigurationProvider("foo", "foo-value"); + var chainedConfig = new ConfigurationRoot(new IConfigurationProvider[] { + provider + }); + + var config = new Config(); + + config.AddConfiguration(chainedConfig, shouldDisposeConfiguration: shouldDispose); + + Assert.False(provider.IsDisposed); + + config.Dispose(); + + Assert.Equal(shouldDispose, provider.IsDisposed); + } + + [Fact] + public void LoadAndCombineKeyValuePairsFromDifferentConfigurationProviders() + { + // Arrange + var dic1 = new Dictionary() + { + {"Mem1:KeyInMem1", "ValueInMem1"} + }; + var dic2 = new Dictionary() + { + {"Mem2:KeyInMem2", "ValueInMem2"} + }; + var dic3 = new Dictionary() + { + {"Mem3:KeyInMem3", "ValueInMem3"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; + + var config = new Config(); + IConfigurationBuilder configurationBuilder = config; + + // Act + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + + var memVal1 = config["mem1:keyinmem1"]; + var memVal2 = config["Mem2:KeyInMem2"]; + var memVal3 = config["MEM3:KEYINMEM3"]; + + // Assert + Assert.Contains(memConfigSrc1, configurationBuilder.Sources); + Assert.Contains(memConfigSrc2, configurationBuilder.Sources); + Assert.Contains(memConfigSrc3, configurationBuilder.Sources); + + Assert.Equal("ValueInMem1", memVal1); + Assert.Equal("ValueInMem2", memVal2); + Assert.Equal("ValueInMem3", memVal3); + + Assert.Equal("ValueInMem1", config["mem1:keyinmem1"]); + Assert.Equal("ValueInMem2", config["Mem2:KeyInMem2"]); + Assert.Equal("ValueInMem3", config["MEM3:KEYINMEM3"]); + Assert.Null(config["NotExist"]); + } + + [Fact] + public void CanChainConfiguration() + { + // Arrange + var dic1 = new Dictionary() + { + {"Mem1:KeyInMem1", "ValueInMem1"} + }; + var dic2 = new Dictionary() + { + {"Mem2:KeyInMem2", "ValueInMem2"} + }; + var dic3 = new Dictionary() + { + {"Mem3:KeyInMem3", "ValueInMem3"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; + + var config = new Config(); + IConfigurationBuilder configurationBuilder = config; + + // Act + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + + var chained = new ConfigurationBuilder().AddConfiguration(config).Build(); + var memVal1 = chained["mem1:keyinmem1"]; + var memVal2 = chained["Mem2:KeyInMem2"]; + var memVal3 = chained["MEM3:KEYINMEM3"]; + + // Assert + + Assert.Equal("ValueInMem1", memVal1); + Assert.Equal("ValueInMem2", memVal2); + Assert.Equal("ValueInMem3", memVal3); + + Assert.Null(chained["NotExist"]); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ChainedAsEnumerateFlattensIntoDictionaryTest(bool removePath) + { + // Arrange + var dic1 = new Dictionary() + { + {"Mem1", "Value1"}, + {"Mem1:", "NoKeyValue1"}, + {"Mem1:KeyInMem1", "ValueInMem1"}, + {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} + }; + var dic2 = new Dictionary() + { + {"Mem2", "Value2"}, + {"Mem2:", "NoKeyValue2"}, + {"Mem2:KeyInMem2", "ValueInMem2"}, + {"Mem2:KeyInMem2:Deep2", "ValueDeep2"} + }; + var dic3 = new Dictionary() + { + {"Mem3", "Value3"}, + {"Mem3:", "NoKeyValue3"}, + {"Mem3:KeyInMem3", "ValueInMem3"}, + {"Mem3:KeyInMem3:Deep3", "ValueDeep3"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; + + var config1 = new Config(); + IConfigurationBuilder configurationBuilder = config1; + + // Act + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + + var config2 = new Config(); + + config2 + .AddConfiguration(config1) + .Add(memConfigSrc3); + + var dict = config2.AsEnumerable(makePathsRelative: removePath).ToDictionary(k => k.Key, v => v.Value); + + // Assert + Assert.Equal("Value1", dict["Mem1"]); + Assert.Equal("NoKeyValue1", dict["Mem1:"]); + Assert.Equal("ValueDeep1", dict["Mem1:KeyInMem1:Deep1"]); + Assert.Equal("ValueInMem2", dict["Mem2:KeyInMem2"]); + Assert.Equal("Value2", dict["Mem2"]); + Assert.Equal("NoKeyValue2", dict["Mem2:"]); + Assert.Equal("ValueDeep2", dict["Mem2:KeyInMem2:Deep2"]); + Assert.Equal("Value3", dict["Mem3"]); + Assert.Equal("NoKeyValue3", dict["Mem3:"]); + Assert.Equal("ValueInMem3", dict["Mem3:KeyInMem3"]); + Assert.Equal("ValueDeep3", dict["Mem3:KeyInMem3:Deep3"]); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void AsEnumerateFlattensIntoDictionaryTest(bool removePath) + { + // Arrange + var dic1 = new Dictionary() + { + {"Mem1", "Value1"}, + {"Mem1:", "NoKeyValue1"}, + {"Mem1:KeyInMem1", "ValueInMem1"}, + {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} + }; + var dic2 = new Dictionary() + { + {"Mem2", "Value2"}, + {"Mem2:", "NoKeyValue2"}, + {"Mem2:KeyInMem2", "ValueInMem2"}, + {"Mem2:KeyInMem2:Deep2", "ValueDeep2"} + }; + var dic3 = new Dictionary() + { + {"Mem3", "Value3"}, + {"Mem3:", "NoKeyValue3"}, + {"Mem3:KeyInMem3", "ValueInMem3"}, + {"Mem3:KeyInMem3:Deep3", "ValueDeep3"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; + + var config = new Config(); + IConfigurationBuilder configurationBuilder = config; + + // Act + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + var dict = config.AsEnumerable(makePathsRelative: removePath).ToDictionary(k => k.Key, v => v.Value); + + // Assert + Assert.Equal("Value1", dict["Mem1"]); + Assert.Equal("NoKeyValue1", dict["Mem1:"]); + Assert.Equal("ValueDeep1", dict["Mem1:KeyInMem1:Deep1"]); + Assert.Equal("ValueInMem2", dict["Mem2:KeyInMem2"]); + Assert.Equal("Value2", dict["Mem2"]); + Assert.Equal("NoKeyValue2", dict["Mem2:"]); + Assert.Equal("ValueDeep2", dict["Mem2:KeyInMem2:Deep2"]); + Assert.Equal("Value3", dict["Mem3"]); + Assert.Equal("NoKeyValue3", dict["Mem3:"]); + Assert.Equal("ValueInMem3", dict["Mem3:KeyInMem3"]); + Assert.Equal("ValueDeep3", dict["Mem3:KeyInMem3:Deep3"]); + } + + [Fact] + public void AsEnumerateStripsKeyFromChildren() + { + // Arrange + var dic1 = new Dictionary() + { + {"Mem1", "Value1"}, + {"Mem1:", "NoKeyValue1"}, + {"Mem1:KeyInMem1", "ValueInMem1"}, + {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} + }; + var dic2 = new Dictionary() + { + {"Mem2", "Value2"}, + {"Mem2:", "NoKeyValue2"}, + {"Mem2:KeyInMem2", "ValueInMem2"}, + {"Mem2:KeyInMem2:Deep2", "ValueDeep2"} + }; + var dic3 = new Dictionary() + { + {"Mem3", "Value3"}, + {"Mem3:", "NoKeyValue3"}, + {"Mem3:KeyInMem3", "ValueInMem3"}, + {"Mem3:KeyInMem4", "ValueInMem4"}, + {"Mem3:KeyInMem3:Deep3", "ValueDeep3"}, + {"Mem3:KeyInMem3:Deep4", "ValueDeep4"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; + + var config = new Config(); + IConfigurationBuilder configurationBuilder = config; + + // Act + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + + var dict = config.GetSection("Mem1").AsEnumerable(makePathsRelative: true).ToDictionary(k => k.Key, v => v.Value); + Assert.Equal(3, dict.Count); + Assert.Equal("NoKeyValue1", dict[""]); + Assert.Equal("ValueInMem1", dict["KeyInMem1"]); + Assert.Equal("ValueDeep1", dict["KeyInMem1:Deep1"]); + + var dict2 = config.GetSection("Mem2").AsEnumerable(makePathsRelative: true).ToDictionary(k => k.Key, v => v.Value); + Assert.Equal(3, dict2.Count); + Assert.Equal("NoKeyValue2", dict2[""]); + Assert.Equal("ValueInMem2", dict2["KeyInMem2"]); + Assert.Equal("ValueDeep2", dict2["KeyInMem2:Deep2"]); + + var dict3 = config.GetSection("Mem3").AsEnumerable(makePathsRelative: true).ToDictionary(k => k.Key, v => v.Value); + Assert.Equal(5, dict3.Count); + Assert.Equal("NoKeyValue3", dict3[""]); + Assert.Equal("ValueInMem3", dict3["KeyInMem3"]); + Assert.Equal("ValueInMem4", dict3["KeyInMem4"]); + Assert.Equal("ValueDeep3", dict3["KeyInMem3:Deep3"]); + Assert.Equal("ValueDeep4", dict3["KeyInMem3:Deep4"]); + } + + [Fact] + public void NewConfigurationProviderOverridesOldOneWhenKeyIsDuplicated() + { + // Arrange + var dic1 = new Dictionary() + { + {"Key1:Key2", "ValueInMem1"} + }; + var dic2 = new Dictionary() + { + {"Key1:Key2", "ValueInMem2"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + + var config = new Config(); + IConfigurationBuilder configurationBuilder = config; + + // Act + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + + // Assert + Assert.Equal("ValueInMem2", config["Key1:Key2"]); + } + + [Fact] + public void NewConfigurationRootMayBeBuiltFromExistingWithDuplicateKeys() + { + var configurationRoot = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + {"keya:keyb", "valueA"}, + }) + .AddInMemoryCollection(new Dictionary + { + {"KEYA:KEYB", "valueB"} + }) + .Build(); + var newConfigurationRoot = new ConfigurationBuilder() + .AddInMemoryCollection(configurationRoot.AsEnumerable()) + .Build(); + Assert.Equal("valueB", newConfigurationRoot["keya:keyb"]); + } + + [Fact] + public void SettingValueUpdatesAllConfigurationProviders() + { + // Arrange + var dict = new Dictionary() + { + {"Key1", "Value1"}, + {"Key2", "Value2"} + }; + + var memConfigSrc1 = new TestMemorySourceProvider(dict); + var memConfigSrc2 = new TestMemorySourceProvider(dict); + var memConfigSrc3 = new TestMemorySourceProvider(dict); + + var config = new Config(); + IConfigurationBuilder configurationBuilder = config; + + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + + // Act + config["Key1"] = "NewValue1"; + config["Key2"] = "NewValue2"; + + var memConfigProvider1 = memConfigSrc1.Build(configurationBuilder); + var memConfigProvider2 = memConfigSrc2.Build(configurationBuilder); + var memConfigProvider3 = memConfigSrc3.Build(configurationBuilder); + + // Assert + Assert.Equal("NewValue1", config["Key1"]); + Assert.Equal("NewValue1", Get(memConfigProvider1, "Key1")); + Assert.Equal("NewValue1", Get(memConfigProvider2, "Key1")); + Assert.Equal("NewValue1", Get(memConfigProvider3, "Key1")); + Assert.Equal("NewValue2", config["Key2"]); + Assert.Equal("NewValue2", Get(memConfigProvider1, "Key2")); + Assert.Equal("NewValue2", Get(memConfigProvider2, "Key2")); + Assert.Equal("NewValue2", Get(memConfigProvider3, "Key2")); + } + + [Fact] + public void CanGetConfigurationSection() + { + // Arrange + var dic1 = new Dictionary() + { + {"Data:DB1:Connection1", "MemVal1"}, + {"Data:DB1:Connection2", "MemVal2"} + }; + var dic2 = new Dictionary() + { + {"DataSource:DB2:Connection", "MemVal3"} + }; + var dic3 = new Dictionary() + { + {"Data", "MemVal4"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; + + var config = new Config(); + IConfigurationBuilder configurationBuilder = config; + + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + + // Act + var configFocus = config.GetSection("Data"); + + var memVal1 = configFocus["DB1:Connection1"]; + var memVal2 = configFocus["DB1:Connection2"]; + var memVal3 = configFocus["DB2:Connection"]; + var memVal4 = configFocus["Source:DB2:Connection"]; + var memVal5 = configFocus.Value; + + // Assert + Assert.Equal("MemVal1", memVal1); + Assert.Equal("MemVal2", memVal2); + Assert.Equal("MemVal4", memVal5); + + Assert.Equal("MemVal1", configFocus["DB1:Connection1"]); + Assert.Equal("MemVal2", configFocus["DB1:Connection2"]); + Assert.Null(configFocus["DB2:Connection"]); + Assert.Null(configFocus["Source:DB2:Connection"]); + Assert.Equal("MemVal4", configFocus.Value); + } + + [Fact] + public void CanGetConnectionStrings() + { + // Arrange + var dic1 = new Dictionary() + { + {"ConnectionStrings:DB1:Connection1", "MemVal1"}, + {"ConnectionStrings:DB1:Connection2", "MemVal2"} + }; + var dic2 = new Dictionary() + { + {"ConnectionStrings:DB2:Connection", "MemVal3"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + + var config = new Config(); + IConfigurationBuilder configurationBuilder = config; + + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + + // Act + var memVal1 = config.GetConnectionString("DB1:Connection1"); + var memVal2 = config.GetConnectionString("DB1:Connection2"); + var memVal3 = config.GetConnectionString("DB2:Connection"); + + // Assert + Assert.Equal("MemVal1", memVal1); + Assert.Equal("MemVal2", memVal2); + Assert.Equal("MemVal3", memVal3); + } + + [Fact] + public void CanGetConfigurationChildren() + { + // Arrange + var dic1 = new Dictionary() + { + {"Data:DB1:Connection1", "MemVal1"}, + {"Data:DB1:Connection2", "MemVal2"} + }; + var dic2 = new Dictionary() + { + {"Data:DB2Connection", "MemVal3"} + }; + var dic3 = new Dictionary() + { + {"DataSource:DB3:Connection", "MemVal4"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; + + var config = new Config(); + IConfigurationBuilder configurationBuilder = config; + + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + + // Act + var configSections = config.GetSection("Data").GetChildren().ToList(); + + // Assert + Assert.Equal(2, configSections.Count()); + Assert.Equal("MemVal1", configSections.FirstOrDefault(c => c.Key == "DB1")["Connection1"]); + Assert.Equal("MemVal2", configSections.FirstOrDefault(c => c.Key == "DB1")["Connection2"]); + Assert.Equal("MemVal3", configSections.FirstOrDefault(c => c.Key == "DB2Connection").Value); + Assert.False(configSections.Exists(c => c.Key == "DB3")); + Assert.False(configSections.Exists(c => c.Key == "DB3")); + } + + [Fact] + public void SourcesReturnsAddedConfigurationProviders() + { + // Arrange + var dict = new Dictionary() + { + {"Mem:KeyInMem", "MemVal"} + }; + var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dict }; + var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dict }; + var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dict }; + + var config = new Config(); + IConfigurationBuilder configurationBuilder = config; + + // Act + + // A MemoryConfigurationSource is added by default, so there will be no error unless we clear it + configurationBuilder.Sources.Clear(); + configurationBuilder.Add(memConfigSrc1); + configurationBuilder.Add(memConfigSrc2); + configurationBuilder.Add(memConfigSrc3); + + // Assert + Assert.Equal(new[] { memConfigSrc1, memConfigSrc2, memConfigSrc3 }, configurationBuilder.Sources); + } + + [Fact] + public void SetValueThrowsExceptionNoSourceRegistered() + { + // Arrange + var config = new Config(); + + // A MemoryConfigurationSource is added by default, so there will be no error unless we clear it + config["Title"] = "Welcome"; + + ((IConfigurationBuilder)config).Sources.Clear(); + + var expectedMsg = "A configuration source is not registered. Please register one before setting a value."; + + // Act + var ex = Assert.Throws(() => config["Title"] = "Welcome"); + + // Assert + Assert.Equal(expectedMsg, ex.Message); + } + + [Fact] + public void SameReloadTokenIsReturnedRepeatedly() + { + // Arrange + IConfiguration config = new Config(); + + // Act + var token1 = config.GetReloadToken(); + var token2 = config.GetReloadToken(); + + // Assert + Assert.Same(token1, token2); + } + + [Fact] + public void DifferentReloadTokenReturnedAfterReloading() + { + // Arrange + IConfigurationRoot config = new Config(); + + // Act + var token1 = config.GetReloadToken(); + var token2 = config.GetReloadToken(); + config.Reload(); + var token3 = config.GetReloadToken(); + var token4 = config.GetReloadToken(); + + // Assert + Assert.Same(token1, token2); + Assert.Same(token3, token4); + Assert.NotSame(token1, token3); + } + + [Fact] + public void TokenTriggeredWhenReloadOccurs() + { + // Arrange + IConfigurationRoot config = new Config(); + + // Act + var token1 = config.GetReloadToken(); + var hasChanged1 = token1.HasChanged; + config.Reload(); + var hasChanged2 = token1.HasChanged; + + // Assert + Assert.False(hasChanged1); + Assert.True(hasChanged2); + } + + [Fact] + public void MultipleCallbacksCanBeRegisteredToReload() + { + // Arrange + IConfigurationRoot config = new Config(); + + // Act + var token1 = config.GetReloadToken(); + var called1 = 0; + token1.RegisterChangeCallback(_ => called1++, state: null); + var called2 = 0; + token1.RegisterChangeCallback(_ => called2++, state: null); + + // Assert + Assert.Equal(0, called1); + Assert.Equal(0, called2); + + config.Reload(); + Assert.Equal(1, called1); + Assert.Equal(1, called2); + + var token2 = config.GetReloadToken(); + var cleanup1 = token2.RegisterChangeCallback(_ => called1++, state: null); + token2.RegisterChangeCallback(_ => called2++, state: null); + + cleanup1.Dispose(); + + config.Reload(); + Assert.Equal(1, called1); + Assert.Equal(2, called2); + } + + [Fact] + public void NewTokenAfterReloadIsNotChanged() + { + // Arrange + IConfigurationRoot config = new Config(); + + // Act + var token1 = config.GetReloadToken(); + var hasChanged1 = token1.HasChanged; + config.Reload(); + var hasChanged2 = token1.HasChanged; + var token2 = config.GetReloadToken(); + var hasChanged3 = token2.HasChanged; + + // + // Assert + Assert.False(hasChanged1); + Assert.True(hasChanged2); + Assert.False(hasChanged3); + Assert.NotSame(token1, token2); + } + + [Fact] + public void KeyStartingWithColonMeansFirstSectionHasEmptyName() + { + // Arrange + var dict = new Dictionary + { + [":Key2"] = "value" + }; + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddInMemoryCollection(dict); + var config = configurationBuilder.Build(); + + // Act + var children = config.GetChildren().ToArray(); + + // Assert + Assert.Single(children); + Assert.Equal(string.Empty, children.First().Key); + Assert.Single(children.First().GetChildren()); + Assert.Equal("Key2", children.First().GetChildren().First().Key); + } + + [Fact] + public void KeyWithDoubleColonHasSectionWithEmptyName() + { + // Arrange + var dict = new Dictionary + { + ["Key1::Key3"] = "value" + }; + + var config = new Config(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var children = config.GetChildren().ToArray(); + + // Assert + Assert.Single(children); + Assert.Equal("Key1", children.First().Key); + Assert.Single(children.First().GetChildren()); + Assert.Equal(string.Empty, children.First().GetChildren().First().Key); + Assert.Single(children.First().GetChildren().First().GetChildren()); + Assert.Equal("Key3", children.First().GetChildren().First().GetChildren().First().Key); + } + + [Fact] + public void KeyEndingWithColonMeansLastSectionHasEmptyName() + { + // Arrange + var dict = new Dictionary + { + ["Key1:"] = "value" + }; + + var config = new Config(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var children = config.GetChildren().ToArray(); + + // Assert + Assert.Single(children); + Assert.Equal("Key1", children.First().Key); + Assert.Single(children.First().GetChildren()); + Assert.Equal(string.Empty, children.First().GetChildren().First().Key); + } + + [Fact] + public void SectionWithValueExists() + { + // Arrange + var dict = new Dictionary() + { + {"Mem1", "Value1"}, + {"Mem1:KeyInMem1", "ValueInMem1"}, + {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} + }; + + var config = new Config(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var sectionExists1 = config.GetSection("Mem1").Exists(); + var sectionExists2 = config.GetSection("Mem1:KeyInMem1").Exists(); + var sectionNotExists = config.GetSection("Mem2").Exists(); + + // Assert + Assert.True(sectionExists1); + Assert.True(sectionExists2); + Assert.False(sectionNotExists); + } + + [Fact] + public void SectionGetRequiredSectionSuccess() + { + // Arrange + var dict = new Dictionary() + { + {"Mem1", "Value1"}, + {"Mem1:KeyInMem1", "ValueInMem1"}, + {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} + }; + + var config = new Config(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var sectionExists1 = config.GetRequiredSection("Mem1").Exists(); + var sectionExists2 = config.GetRequiredSection("Mem1:KeyInMem1").Exists(); + + // Assert + Assert.True(sectionExists1); + Assert.True(sectionExists2); + } + + [Fact] + public void SectionGetRequiredSectionMissingThrowException() + { + // Arrange + var dict = new Dictionary() + { + {"Mem1", "Value1"}, + {"Mem1:Deep1", "Value1"}, + }; + + var config = new Config(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + Assert.Throws(() => config.GetRequiredSection("Mem2")); + Assert.Throws(() => config.GetRequiredSection("Mem1:Deep2")); + } + + [Fact] + public void SectionWithChildrenExists() + { + // Arrange + var dict = new Dictionary() + { + {"Mem1:KeyInMem1", "ValueInMem1"}, + {"Mem1:KeyInMem1:Deep1", "ValueDeep1"}, + {"Mem2:KeyInMem2:Deep1", "ValueDeep2"} + }; + + var config = new Config(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var sectionExists1 = config.GetSection("Mem1").Exists(); + var sectionExists2 = config.GetSection("Mem2").Exists(); + var sectionNotExists = config.GetSection("Mem3").Exists(); + + // Assert + Assert.True(sectionExists1); + Assert.True(sectionExists2); + Assert.False(sectionNotExists); + } + + [Theory] + [InlineData("Value1")] + [InlineData("")] + public void KeyWithValueAndWithoutChildrenExistsAsSection(string value) + { + // Arrange + var dict = new Dictionary() + { + {"Mem1", value} + }; + + var config = new Config(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var sectionExists = config.GetSection("Mem1").Exists(); + + // Assert + Assert.True(sectionExists); + } + + [Fact] + public void KeyWithNullValueAndWithoutChildrenIsASectionButNotExists() + { + // Arrange + var dict = new Dictionary() + { + {"Mem1", null} + }; + + var config = new Config(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var sections = config.GetChildren(); + var sectionExists = config.GetSection("Mem1").Exists(); + var sectionChildren = config.GetSection("Mem1").GetChildren(); + + // Assert + Assert.Single(sections, section => section.Key == "Mem1"); + Assert.False(sectionExists); + Assert.Empty(sectionChildren); + } + + [Fact] + public void SectionWithChildrenHasNullValue() + { + // Arrange + var dict = new Dictionary() + { + {"Mem1:KeyInMem1", "ValueInMem1"}, + }; + + + var config = new Config(); + ((IConfigurationBuilder)config).AddInMemoryCollection(dict); + + // Act + var sectionValue = config.GetSection("Mem1").Value; + + // Assert + Assert.Null(sectionValue); + } + + [Fact] + public void ProviderWithNullReloadToken() + { + // Arrange + var config = new Config(); + IConfigurationBuilder builder = config; + + // Assert + Assert.NotNull(builder.Build()); + } + + [Fact] + public void BuildReturnsThis() + { + // Arrange + var config = new Config(); + + // Assert + Assert.Same(config, ((IConfigurationBuilder)config).Build()); + } + + private static string Get(IConfigurationProvider provider, string key) + { + string value; + + if (!provider.TryGet(key, out value)) + { + throw new InvalidOperationException("Key not found"); + } + + return value; + } + + private class TestConfigurationSource : IConfigurationSource + { + private readonly IConfigurationProvider _provider; + + public TestConfigurationSource(IConfigurationProvider provider) + { + _provider = provider; + } + + public IConfigurationProvider Build(IConfigurationBuilder builder) + { + return _provider; + } + } + + private class TestConfigurationProvider : ConfigurationProvider + { + public TestConfigurationProvider(string key, string value) + => Data.Add(key, value); + } + + private class DisposableTestConfigurationProvider : ConfigurationProvider, IDisposable + { + public bool IsDisposed { get; set; } + + public DisposableTestConfigurationProvider(string key, string value) + => Data.Add(key, value); + + public void Dispose() + => IsDisposed = true; + } + + private class TestChangeToken : IChangeToken + { + public List<(Action, object)> Callbacks { get; } = new List<(Action, object)>(); + + public bool HasChanged => false; + + public bool ActiveChangeCallbacks => true; + + public IDisposable RegisterChangeCallback(Action callback, object state) + { + var item = (callback, state); + Callbacks.Add(item); + return new DisposableAction(() => Callbacks.Remove(item)); + } + + private class DisposableAction : IDisposable + { + private Action _action; + + public DisposableAction(Action action) + { + _action = action; + } + + public void Dispose() + { + var a = _action; + if (a != null) + { + _action = null; + a(); + } + } + } + } + + private class TestMemorySourceProvider : MemoryConfigurationProvider, IConfigurationSource + { + public TestMemorySourceProvider(Dictionary initialData) + : base(new MemoryConfigurationSource { InitialData = initialData }) + { } + + public IConfigurationProvider Build(IConfigurationBuilder builder) + { + return this; + } + } + + private class NullReloadTokenConfigSource : IConfigurationSource, IConfigurationProvider + { + public IEnumerable GetChildKeys(IEnumerable earlierKeys, string parentPath) => throw new NotImplementedException(); + public IChangeToken GetReloadToken() => null; + public void Load() { } + public void Set(string key, string value) => throw new NotImplementedException(); + public bool TryGet(string key, out string value) => throw new NotImplementedException(); + public IConfigurationProvider Build(IConfigurationBuilder builder) => this; + } + + } +} From eb99621dbd4ead612d273994845b144890f544ff Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Thu, 8 Jul 2021 08:40:44 -0700 Subject: [PATCH 2/6] Use resource string --- .../Microsoft.Extensions.Configuration/src/Config.cs | 2 +- .../Microsoft.Extensions.Configuration/tests/ConfigTests.cs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/Config.cs b/src/libraries/Microsoft.Extensions.Configuration/src/Config.cs index 4f48304ae7f81..9fcef66985d01 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/Config.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/Config.cs @@ -64,7 +64,7 @@ public Config() { if (_providers.Count == 0) { - throw new InvalidOperationException("A configuration source is not registered. Please register one before setting a value."); + throw new InvalidOperationException(SR.Error_NoSources); } foreach (var provider in _providers) diff --git a/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs b/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs index 7a39c8fa9188d..70ab943026933 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs @@ -740,13 +740,11 @@ public void SetValueThrowsExceptionNoSourceRegistered() ((IConfigurationBuilder)config).Sources.Clear(); - var expectedMsg = "A configuration source is not registered. Please register one before setting a value."; - // Act var ex = Assert.Throws(() => config["Title"] = "Welcome"); // Assert - Assert.Equal(expectedMsg, ex.Message); + Assert.Equal(SR.Error_NoSources, ex.Message); } [Fact] From 5f067dc192f6a1e408b35ab5d4ba9d3f67ed8686 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Mon, 12 Jul 2021 08:53:44 -0700 Subject: [PATCH 3/6] Share some logic with ConfigurationRoot --- .../src/Config.cs | 29 +--------- .../src/ConfigurationRoot.cs | 56 ++++++++++--------- 2 files changed, 33 insertions(+), 52 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/Config.cs b/src/libraries/Microsoft.Extensions.Configuration/src/Config.cs index 9fcef66985d01..138d6e532032b 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/Config.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/Config.cs @@ -45,32 +45,14 @@ public Config() { lock (_providerLock) { - for (int i = _providers.Count - 1; i >= 0; i--) - { - var provider = _providers[i]; - - if (provider.TryGet(key, out string value)) - { - return value; - } - } - - return null; + return ConfigurationRoot.GetConfiguration(_providers, key); } } set { lock (_providerLock) { - if (_providers.Count == 0) - { - throw new InvalidOperationException(SR.Error_NoSources); - } - - foreach (var provider in _providers) - { - provider.Set(key, value); - } + ConfigurationRoot.SetConfiguration(_providers, key, value); } } } @@ -84,12 +66,7 @@ public IEnumerable GetChildren() lock (_providerLock) { // ToList() to eagerly evaluate inside lock. - return _providers - .Aggregate(Enumerable.Empty(), - static (seed, source) => source.GetChildKeys(seed, parentPath: null)) - .Distinct(StringComparer.OrdinalIgnoreCase) - .Select(GetSection) - .ToList(); + return this.GetChildrenImplementation(null).ToList(); } } diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationRoot.cs b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationRoot.cs index 455efdb4176bf..cfe56a20978e5 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationRoot.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationRoot.cs @@ -49,32 +49,8 @@ public ConfigurationRoot(IList providers) /// The configuration value. public string this[string key] { - get - { - for (int i = _providers.Count - 1; i >= 0; i--) - { - IConfigurationProvider provider = _providers[i]; - - if (provider.TryGet(key, out string value)) - { - return value; - } - } - - return null; - } - set - { - if (_providers.Count == 0) - { - throw new InvalidOperationException(SR.Error_NoSources); - } - - foreach (IConfigurationProvider provider in _providers) - { - provider.Set(key, value); - } - } + get => GetConfiguration(_providers, key); + set => SetConfiguration(_providers, key, value); } /// @@ -134,5 +110,33 @@ public void Dispose() (provider as IDisposable)?.Dispose(); } } + + internal static string GetConfiguration(IList providers, string key) + { + for (int i = providers.Count - 1; i >= 0; i--) + { + IConfigurationProvider provider = providers[i]; + + if (provider.TryGet(key, out string value)) + { + return value; + } + } + + return null; + } + + internal static void SetConfiguration(IList providers, string key, string value) + { + if (providers.Count == 0) + { + throw new InvalidOperationException(SR.Error_NoSources); + } + + foreach (IConfigurationProvider provider in providers) + { + provider.Set(key, value); + } + } } } From 0f7448c6f9774689800caa95a6f8ec4d32b97456 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Mon, 12 Jul 2021 12:53:22 -0700 Subject: [PATCH 4/6] Remove "Maybe we should change that" comment --- .../Microsoft.Extensions.Configuration/tests/ConfigTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs b/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs index 70ab943026933..73084dce146d6 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs @@ -65,7 +65,7 @@ public void SettingConfigValuesDoesNotTriggerReloadToken() Assert.Equal("TestValue", config["TestKey"]); - // ConfigurationRoot doesn't fire the token today when the setter is called. Maybe we should change that. + // ConfigurationRoot doesn't fire the token today when the setter is called. Assert.False(reloadToken.HasChanged); } From c8e28392a74173d6837a8d0ff14adb722c7ef556 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Mon, 12 Jul 2021 13:39:21 -0700 Subject: [PATCH 5/6] ConfigTests -> ConfigTest --- .../tests/{ConfigTests.cs => ConfigTest.cs} | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) rename src/libraries/Microsoft.Extensions.Configuration/tests/{ConfigTests.cs => ConfigTest.cs} (97%) diff --git a/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs b/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTest.cs similarity index 97% rename from src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs rename to src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTest.cs index 73084dce146d6..341f833b53cf2 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTest.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.Configuration.Test { - public class ConfigTests + public class ConfigTest { [Fact] public void AutoUpdates() @@ -307,7 +307,8 @@ public void CanChainConfiguration() configurationBuilder.Add(memConfigSrc2); configurationBuilder.Add(memConfigSrc3); - var chained = new ConfigurationBuilder().AddConfiguration(config).Build(); + var chained = new Config(); + chained.AddConfiguration(config); var memVal1 = chained["mem1:keyinmem1"]; var memVal2 = chained["Mem2:KeyInMem2"]; var memVal3 = chained["MEM3:KEYINMEM3"]; @@ -524,19 +525,21 @@ public void NewConfigurationProviderOverridesOldOneWhenKeyIsDuplicated() [Fact] public void NewConfigurationRootMayBeBuiltFromExistingWithDuplicateKeys() { - var configurationRoot = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - {"keya:keyb", "valueA"}, - }) - .AddInMemoryCollection(new Dictionary - { - {"KEYA:KEYB", "valueB"} - }) - .Build(); - var newConfigurationRoot = new ConfigurationBuilder() - .AddInMemoryCollection(configurationRoot.AsEnumerable()) - .Build(); + var configurationRoot = new Config(); + + configurationRoot.AddInMemoryCollection(new Dictionary + { + {"keya:keyb", "valueA"}, + }); + configurationRoot.AddInMemoryCollection(new Dictionary + { + {"KEYA:KEYB", "valueB"}, + }); + + var newConfigurationRoot = new Config(); + + newConfigurationRoot.AddInMemoryCollection(configurationRoot.AsEnumerable()); + Assert.Equal("valueB", newConfigurationRoot["keya:keyb"]); } @@ -859,9 +862,8 @@ public void KeyStartingWithColonMeansFirstSectionHasEmptyName() { [":Key2"] = "value" }; - var configurationBuilder = new ConfigurationBuilder(); - configurationBuilder.AddInMemoryCollection(dict); - var config = configurationBuilder.Build(); + var config = new Config(); + config.AddInMemoryCollection(dict); // Act var children = config.GetChildren().ToArray(); From 09d747119e9e390d6cc6b184a03c1c6e90010e0e Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Mon, 12 Jul 2021 15:06:03 -0700 Subject: [PATCH 6/6] Config -> ConfigurationManager --- .../ref/Microsoft.Extensions.Configuration.cs | 4 +- .../{Config.cs => ConfigurationManager.cs} | 12 +-- ...figTest.cs => ConfigurationManagerTest.cs} | 89 +++++++++---------- 3 files changed, 52 insertions(+), 53 deletions(-) rename src/libraries/Microsoft.Extensions.Configuration/src/{Config.cs => ConfigurationManager.cs} (96%) rename src/libraries/Microsoft.Extensions.Configuration/tests/{ConfigTest.cs => ConfigurationManagerTest.cs} (94%) diff --git a/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs b/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs index 110c5b61787c0..feef64fc2dde1 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs @@ -28,9 +28,9 @@ public partial class ChainedConfigurationSource : Microsoft.Extensions.Configura public bool ShouldDisposeConfiguration { get { throw null; } set { } } public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; } } - public sealed partial class Config : Microsoft.Extensions.Configuration.IConfigurationBuilder, Microsoft.Extensions.Configuration.IConfigurationRoot, System.IDisposable + public sealed partial class ConfigurationManager : Microsoft.Extensions.Configuration.IConfigurationBuilder, Microsoft.Extensions.Configuration.IConfigurationRoot, System.IDisposable { - public Config() { } + public ConfigurationManager() { } public string this[string key] { get { throw null; } set { throw null; } } public IConfigurationSection GetSection(string key) { throw null; } public System.Collections.Generic.IEnumerable GetChildren() { throw null; } diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/Config.cs b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationManager.cs similarity index 96% rename from src/libraries/Microsoft.Extensions.Configuration/src/Config.cs rename to src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationManager.cs index 138d6e532032b..9578f3c334ac3 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/Config.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationManager.cs @@ -14,7 +14,7 @@ namespace Microsoft.Extensions.Configuration /// Configuration is mutable configuration object. It is both an and an . /// As sources are added, it updates its current view of configuration. Once Build is called, configuration is frozen. /// - public sealed class Config : IConfigurationBuilder, IConfigurationRoot, IDisposable + public sealed class ConfigurationManager : IConfigurationBuilder, IConfigurationRoot, IDisposable { private readonly ConfigurationSources _sources; private readonly ConfigurationBuilderProperties _properties; @@ -27,7 +27,7 @@ public sealed class Config : IConfigurationBuilder, IConfigurationRoot, IDisposa /// /// Creates an empty mutable configuration object that is both an and an . /// - public Config() + public ConfigurationManager() { _sources = new ConfigurationSources(this); _properties = new ConfigurationBuilderProperties(this); @@ -181,9 +181,9 @@ private void DisposeRegistrationsAndProvidersUnsynchronized() private class ConfigurationSources : IList { private readonly List _sources = new(); - private readonly Config _config; + private readonly ConfigurationManager _config; - public ConfigurationSources(Config config) + public ConfigurationSources(ConfigurationManager config) { _config = config; } @@ -262,9 +262,9 @@ IEnumerator IEnumerable.GetEnumerator() private class ConfigurationBuilderProperties : IDictionary { private readonly Dictionary _properties = new(); - private readonly Config _config; + private readonly ConfigurationManager _config; - public ConfigurationBuilderProperties(Config config) + public ConfigurationBuilderProperties(ConfigurationManager config) { _config = config; } diff --git a/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTest.cs b/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigurationManagerTest.cs similarity index 94% rename from src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTest.cs rename to src/libraries/Microsoft.Extensions.Configuration/tests/ConfigurationManagerTest.cs index 341f833b53cf2..6e7a4919b25e4 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigTest.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/tests/ConfigurationManagerTest.cs @@ -11,12 +11,12 @@ namespace Microsoft.Extensions.Configuration.Test { - public class ConfigTest + public class ConfigurationManagerTest { [Fact] public void AutoUpdates() { - var config = new Config(); + var config = new ConfigurationManager(); config.AddInMemoryCollection(new Dictionary { @@ -29,7 +29,7 @@ public void AutoUpdates() [Fact] public void TriggersReloadTokenOnSourceAddition() { - var config = new Config(); + var config = new ConfigurationManager(); var reloadToken = ((IConfiguration)config).GetReloadToken(); @@ -43,11 +43,10 @@ public void TriggersReloadTokenOnSourceAddition() Assert.True(reloadToken.HasChanged); } - [Fact] public void SettingValuesWorksWithoutManuallyAddingSource() { - var config = new Config + var config = new ConfigurationManager { ["TestKey"] = "TestValue", }; @@ -58,7 +57,7 @@ public void SettingValuesWorksWithoutManuallyAddingSource() [Fact] public void SettingConfigValuesDoesNotTriggerReloadToken() { - var config = new Config(); + var config = new ConfigurationManager(); var reloadToken = ((IConfiguration)config).GetReloadToken(); config["TestKey"] = "TestValue"; @@ -72,7 +71,7 @@ public void SettingConfigValuesDoesNotTriggerReloadToken() [Fact] public void SettingIConfigurationBuilderPropertiesReloadsSources() { - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder configBuilder = config; config["PreReloadTestConfigKey"] = "PreReloadTestConfigValue"; @@ -104,7 +103,7 @@ public void DisposesProvidersOnDispose() var provider4 = new DisposableTestConfigurationProvider("qux", "qux-value"); var provider5 = new DisposableTestConfigurationProvider("quux", "quux-value"); - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder builder = config; builder.Add(new TestConfigurationSource(provider1)); @@ -141,7 +140,7 @@ public void DisposesProvidersOnRemoval() var source4 = new TestConfigurationSource(provider4); var source5 = new TestConfigurationSource(provider5); - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder builder = config; builder.Add(source1); @@ -179,7 +178,7 @@ public void DisposesChangeTokenRegistrationsOnDispose() var providerMock = new Mock(); providerMock.Setup(p => p.GetReloadToken()).Returns(changeToken); - var config = new Config(); + var config = new ConfigurationManager(); ((IConfigurationBuilder)config).Add(new TestConfigurationSource(providerMock.Object)); @@ -199,7 +198,7 @@ public void DisposesChangeTokenRegistrationsOnRemoval() var source = new TestConfigurationSource(providerMock.Object); - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder builder = config; builder.Add(source); @@ -221,7 +220,7 @@ public void ChainedConfigurationIsDisposedOnDispose(bool shouldDispose) provider }); - var config = new Config(); + var config = new ConfigurationManager(); config.AddConfiguration(chainedConfig, shouldDisposeConfiguration: shouldDispose); @@ -252,7 +251,7 @@ public void LoadAndCombineKeyValuePairsFromDifferentConfigurationProviders() var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder configurationBuilder = config; // Act @@ -299,7 +298,7 @@ public void CanChainConfiguration() var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder configurationBuilder = config; // Act @@ -307,7 +306,7 @@ public void CanChainConfiguration() configurationBuilder.Add(memConfigSrc2); configurationBuilder.Add(memConfigSrc3); - var chained = new Config(); + var chained = new ConfigurationManager(); chained.AddConfiguration(config); var memVal1 = chained["mem1:keyinmem1"]; var memVal2 = chained["Mem2:KeyInMem2"]; @@ -353,14 +352,14 @@ public void ChainedAsEnumerateFlattensIntoDictionaryTest(bool removePath) var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; - var config1 = new Config(); + var config1 = new ConfigurationManager(); IConfigurationBuilder configurationBuilder = config1; // Act configurationBuilder.Add(memConfigSrc1); configurationBuilder.Add(memConfigSrc2); - var config2 = new Config(); + var config2 = new ConfigurationManager(); config2 .AddConfiguration(config1) @@ -413,7 +412,7 @@ public void AsEnumerateFlattensIntoDictionaryTest(bool removePath) var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder configurationBuilder = config; // Act @@ -467,7 +466,7 @@ public void AsEnumerateStripsKeyFromChildren() var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder configurationBuilder = config; // Act @@ -511,7 +510,7 @@ public void NewConfigurationProviderOverridesOldOneWhenKeyIsDuplicated() var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder configurationBuilder = config; // Act @@ -525,7 +524,7 @@ public void NewConfigurationProviderOverridesOldOneWhenKeyIsDuplicated() [Fact] public void NewConfigurationRootMayBeBuiltFromExistingWithDuplicateKeys() { - var configurationRoot = new Config(); + var configurationRoot = new ConfigurationManager(); configurationRoot.AddInMemoryCollection(new Dictionary { @@ -536,7 +535,7 @@ public void NewConfigurationRootMayBeBuiltFromExistingWithDuplicateKeys() {"KEYA:KEYB", "valueB"}, }); - var newConfigurationRoot = new Config(); + var newConfigurationRoot = new ConfigurationManager(); newConfigurationRoot.AddInMemoryCollection(configurationRoot.AsEnumerable()); @@ -557,7 +556,7 @@ public void SettingValueUpdatesAllConfigurationProviders() var memConfigSrc2 = new TestMemorySourceProvider(dict); var memConfigSrc3 = new TestMemorySourceProvider(dict); - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder configurationBuilder = config; configurationBuilder.Add(memConfigSrc1); @@ -604,7 +603,7 @@ public void CanGetConfigurationSection() var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder configurationBuilder = config; configurationBuilder.Add(memConfigSrc1); @@ -648,7 +647,7 @@ public void CanGetConnectionStrings() var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder configurationBuilder = config; configurationBuilder.Add(memConfigSrc1); @@ -686,7 +685,7 @@ public void CanGetConfigurationChildren() var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder configurationBuilder = config; configurationBuilder.Add(memConfigSrc1); @@ -717,7 +716,7 @@ public void SourcesReturnsAddedConfigurationProviders() var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dict }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dict }; - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder configurationBuilder = config; // Act @@ -736,7 +735,7 @@ public void SourcesReturnsAddedConfigurationProviders() public void SetValueThrowsExceptionNoSourceRegistered() { // Arrange - var config = new Config(); + var config = new ConfigurationManager(); // A MemoryConfigurationSource is added by default, so there will be no error unless we clear it config["Title"] = "Welcome"; @@ -754,7 +753,7 @@ public void SetValueThrowsExceptionNoSourceRegistered() public void SameReloadTokenIsReturnedRepeatedly() { // Arrange - IConfiguration config = new Config(); + IConfiguration config = new ConfigurationManager(); // Act var token1 = config.GetReloadToken(); @@ -768,7 +767,7 @@ public void SameReloadTokenIsReturnedRepeatedly() public void DifferentReloadTokenReturnedAfterReloading() { // Arrange - IConfigurationRoot config = new Config(); + IConfigurationRoot config = new ConfigurationManager(); // Act var token1 = config.GetReloadToken(); @@ -787,7 +786,7 @@ public void DifferentReloadTokenReturnedAfterReloading() public void TokenTriggeredWhenReloadOccurs() { // Arrange - IConfigurationRoot config = new Config(); + IConfigurationRoot config = new ConfigurationManager(); // Act var token1 = config.GetReloadToken(); @@ -804,7 +803,7 @@ public void TokenTriggeredWhenReloadOccurs() public void MultipleCallbacksCanBeRegisteredToReload() { // Arrange - IConfigurationRoot config = new Config(); + IConfigurationRoot config = new ConfigurationManager(); // Act var token1 = config.GetReloadToken(); @@ -836,7 +835,7 @@ public void MultipleCallbacksCanBeRegisteredToReload() public void NewTokenAfterReloadIsNotChanged() { // Arrange - IConfigurationRoot config = new Config(); + IConfigurationRoot config = new ConfigurationManager(); // Act var token1 = config.GetReloadToken(); @@ -862,7 +861,7 @@ public void KeyStartingWithColonMeansFirstSectionHasEmptyName() { [":Key2"] = "value" }; - var config = new Config(); + var config = new ConfigurationManager(); config.AddInMemoryCollection(dict); // Act @@ -884,7 +883,7 @@ public void KeyWithDoubleColonHasSectionWithEmptyName() ["Key1::Key3"] = "value" }; - var config = new Config(); + var config = new ConfigurationManager(); ((IConfigurationBuilder)config).AddInMemoryCollection(dict); // Act @@ -908,7 +907,7 @@ public void KeyEndingWithColonMeansLastSectionHasEmptyName() ["Key1:"] = "value" }; - var config = new Config(); + var config = new ConfigurationManager(); ((IConfigurationBuilder)config).AddInMemoryCollection(dict); // Act @@ -932,7 +931,7 @@ public void SectionWithValueExists() {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} }; - var config = new Config(); + var config = new ConfigurationManager(); ((IConfigurationBuilder)config).AddInMemoryCollection(dict); // Act @@ -957,7 +956,7 @@ public void SectionGetRequiredSectionSuccess() {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} }; - var config = new Config(); + var config = new ConfigurationManager(); ((IConfigurationBuilder)config).AddInMemoryCollection(dict); // Act @@ -979,7 +978,7 @@ public void SectionGetRequiredSectionMissingThrowException() {"Mem1:Deep1", "Value1"}, }; - var config = new Config(); + var config = new ConfigurationManager(); ((IConfigurationBuilder)config).AddInMemoryCollection(dict); Assert.Throws(() => config.GetRequiredSection("Mem2")); @@ -997,7 +996,7 @@ public void SectionWithChildrenExists() {"Mem2:KeyInMem2:Deep1", "ValueDeep2"} }; - var config = new Config(); + var config = new ConfigurationManager(); ((IConfigurationBuilder)config).AddInMemoryCollection(dict); // Act @@ -1022,7 +1021,7 @@ public void KeyWithValueAndWithoutChildrenExistsAsSection(string value) {"Mem1", value} }; - var config = new Config(); + var config = new ConfigurationManager(); ((IConfigurationBuilder)config).AddInMemoryCollection(dict); // Act @@ -1041,7 +1040,7 @@ public void KeyWithNullValueAndWithoutChildrenIsASectionButNotExists() {"Mem1", null} }; - var config = new Config(); + var config = new ConfigurationManager(); ((IConfigurationBuilder)config).AddInMemoryCollection(dict); // Act @@ -1065,7 +1064,7 @@ public void SectionWithChildrenHasNullValue() }; - var config = new Config(); + var config = new ConfigurationManager(); ((IConfigurationBuilder)config).AddInMemoryCollection(dict); // Act @@ -1079,7 +1078,7 @@ public void SectionWithChildrenHasNullValue() public void ProviderWithNullReloadToken() { // Arrange - var config = new Config(); + var config = new ConfigurationManager(); IConfigurationBuilder builder = config; // Assert @@ -1090,7 +1089,7 @@ public void ProviderWithNullReloadToken() public void BuildReturnsThis() { // Arrange - var config = new Config(); + var config = new ConfigurationManager(); // Assert Assert.Same(config, ((IConfigurationBuilder)config).Build());