diff --git a/src/Microsoft.Framework.Configuration.Abstractions/IConfigurationSection.cs b/src/Microsoft.Framework.Configuration.Abstractions/IConfigurationSection.cs index e2781661..9f32c04a 100644 --- a/src/Microsoft.Framework.Configuration.Abstractions/IConfigurationSection.cs +++ b/src/Microsoft.Framework.Configuration.Abstractions/IConfigurationSection.cs @@ -7,6 +7,7 @@ namespace Microsoft.Framework.Configuration public interface IConfigurationSection : IConfiguration { string Key { get; } + string Path { get; } string Value { get; set; } } } diff --git a/src/Microsoft.Framework.Configuration.Abstractions/IConfigurationSource.cs b/src/Microsoft.Framework.Configuration.Abstractions/IConfigurationSource.cs index eab05251..85ce9211 100644 --- a/src/Microsoft.Framework.Configuration.Abstractions/IConfigurationSource.cs +++ b/src/Microsoft.Framework.Configuration.Abstractions/IConfigurationSource.cs @@ -15,7 +15,7 @@ public interface IConfigurationSource IEnumerable ProduceConfigurationSections( IEnumerable earlierKeys, - string prefix, + string parentPath, string delimiter); } } \ No newline at end of file diff --git a/src/Microsoft.Framework.Configuration.Binder/ConfigurationBinder.cs b/src/Microsoft.Framework.Configuration.Binder/ConfigurationBinder.cs index b9fbe90c..0ab37785 100644 --- a/src/Microsoft.Framework.Configuration.Binder/ConfigurationBinder.cs +++ b/src/Microsoft.Framework.Configuration.Binder/ConfigurationBinder.cs @@ -155,13 +155,6 @@ private static void BindDictionary(object dictionary, Type dictionaryType, IConf if (item != null) { var key = child.Key; - var section = config as IConfigurationSection; - if (section != null) - { - // Remove the parent key and : delimiter to get the configurationSection's key - key = key.Substring(section.Key.Length + 1); - } - addMethod.Invoke(dictionary, new[] { key, item }); } } diff --git a/src/Microsoft.Framework.Configuration/ConfigurationBase.cs b/src/Microsoft.Framework.Configuration/ConfigurationBase.cs index 5786710d..558f79da 100644 --- a/src/Microsoft.Framework.Configuration/ConfigurationBase.cs +++ b/src/Microsoft.Framework.Configuration/ConfigurationBase.cs @@ -21,41 +21,17 @@ public ConfigurationBase(IList sources) _sources = sources; } + public abstract string Path { get; } + public string this[string key] { get { - if (string.IsNullOrEmpty(key)) - { - throw new InvalidOperationException(Resources.Error_EmptyKey); - } - - // If a key in the newly added configuration source is identical to a key in a - // formerly added configuration source, the new one overrides the former one. - // So we search in reverse order, starting with latest configuration source. - foreach (var src in _sources.Reverse()) - { - string value = null; - - if (src.TryGet(GetPrefix() + key, out value)) - { - return value; - } - } - - return null; + return GetSection(key).Value; } set { - if (!Sources.Any()) - { - throw new InvalidOperationException(Resources.Error_NoSources); - } - - foreach (var src in Sources) - { - src.Set(GetPrefix() + key, value); - } + GetSection(key).Value = value; } } @@ -69,29 +45,20 @@ public IList Sources public IEnumerable GetChildren() { - var prefix = GetPrefix(); - var segments = Sources.Aggregate( Enumerable.Empty(), - (seed, source) => source.ProduceConfigurationSections(seed, prefix, Constants.KeyDelimiter)); + (seed, source) => source.ProduceConfigurationSections(seed, Path, Constants.KeyDelimiter)); var distinctSegments = segments.Distinct(); return distinctSegments.Select(segment => { - return new ConfigurationSection(Sources, prefix + segment); + return new ConfigurationSection(Sources, Path, segment); }); } public IConfigurationSection GetSection(string key) { - if (string.IsNullOrEmpty(key)) - { - throw new InvalidOperationException(Resources.Error_EmptyKey); - } - - return new ConfigurationSection(Sources, GetPrefix() + key); + return new ConfigurationSection(Sources, Path, key); } - - protected abstract string GetPrefix(); } } diff --git a/src/Microsoft.Framework.Configuration/ConfigurationRoot.cs b/src/Microsoft.Framework.Configuration/ConfigurationRoot.cs index 98c56d0b..746411e2 100644 --- a/src/Microsoft.Framework.Configuration/ConfigurationRoot.cs +++ b/src/Microsoft.Framework.Configuration/ConfigurationRoot.cs @@ -17,17 +17,20 @@ public ConfigurationRoot(IList sources) } } - public void Reload() + public override string Path { - foreach (var src in Sources) + get { - src.Load(); + return string.Empty; } } - protected override string GetPrefix() + public void Reload() { - return string.Empty; + foreach (var src in Sources) + { + src.Load(); + } } } } diff --git a/src/Microsoft.Framework.Configuration/ConfigurationSection.cs b/src/Microsoft.Framework.Configuration/ConfigurationSection.cs index 25e24d9f..14d466ed 100644 --- a/src/Microsoft.Framework.Configuration/ConfigurationSection.cs +++ b/src/Microsoft.Framework.Configuration/ConfigurationSection.cs @@ -10,26 +10,30 @@ namespace Microsoft.Framework.Configuration public class ConfigurationSection : ConfigurationBase, IConfigurationSection { private readonly string _key; + private readonly string _path; - public ConfigurationSection(IList sources, string key) + public ConfigurationSection(IList sources, string parentPath, string key) : base(sources) { - if (sources == null) + if (parentPath == null) { - throw new ArgumentNullException(nameof(sources)); - } - - if (key == null) - { - throw new ArgumentNullException(nameof(key)); + throw new ArgumentNullException(nameof(parentPath)); } if (string.IsNullOrEmpty(key)) { - throw new InvalidOperationException(Resources.Error_EmptyKey); + throw new ArgumentException(Resources.Error_EmptyKey); } _key = key; + if (!string.Equals(parentPath, string.Empty)) + { + _path = parentPath + Constants.KeyDelimiter + key; + } + else + { + _path = key; + } } public string Key @@ -40,6 +44,14 @@ public string Key } } + public override string Path + { + get + { + return _path; + } + } + public string Value { get @@ -48,7 +60,7 @@ public string Value { string value = null; - if (src.TryGet(_key, out value)) + if (src.TryGet(Path, out value)) { return value; } @@ -65,14 +77,9 @@ public string Value foreach (var src in Sources) { - src.Set(Key, value); + src.Set(Path, value); } } } - - protected override string GetPrefix() - { - return _key + Constants.KeyDelimiter; - } } } diff --git a/src/Microsoft.Framework.Configuration/ConfigurationSource.cs b/src/Microsoft.Framework.Configuration/ConfigurationSource.cs index eff84ec5..89a08d19 100644 --- a/src/Microsoft.Framework.Configuration/ConfigurationSource.cs +++ b/src/Microsoft.Framework.Configuration/ConfigurationSource.cs @@ -32,20 +32,22 @@ public virtual void Load() public virtual IEnumerable ProduceConfigurationSections( IEnumerable earlierKeys, - string prefix, + string parentPath, string delimiter) { + parentPath = parentPath + delimiter; + return Data - .Where(kv => kv.Key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - .Select(kv => Segment(kv.Key, prefix, delimiter)) + .Where(kv => kv.Key.StartsWith(parentPath, StringComparison.OrdinalIgnoreCase)) + .Select(kv => Segment(kv.Key, parentPath, delimiter)) .Concat(earlierKeys) .OrderBy(k => k, ConfigurationKeyComparer.Instance); } - private static string Segment(string key, string prefix, string delimiter) + private static string Segment(string key, string parentPath, string delimiter) { - var indexOf = key.IndexOf(delimiter, prefix.Length, StringComparison.OrdinalIgnoreCase); - return indexOf < 0 ? key.Substring(prefix.Length) : key.Substring(prefix.Length, indexOf - prefix.Length); + var indexOf = key.IndexOf(delimiter, parentPath.Length, StringComparison.OrdinalIgnoreCase); + return indexOf < 0 ? key.Substring(parentPath.Length) : key.Substring(parentPath.Length, indexOf - parentPath.Length); } } } diff --git a/src/Microsoft.Framework.Configuration/Resources.resx b/src/Microsoft.Framework.Configuration/Resources.resx index 947d6a27..fb7badd0 100644 --- a/src/Microsoft.Framework.Configuration/Resources.resx +++ b/src/Microsoft.Framework.Configuration/Resources.resx @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Empty key is not allowed. + Empty or null key is not allowed. Reload can only be called on top-level configuration element diff --git a/test/Microsoft.Framework.Configuration.FunctionalTests/ArrayTests.cs b/test/Microsoft.Framework.Configuration.FunctionalTests/ArrayTests.cs index 1c7f9f8b..92675f6f 100644 --- a/test/Microsoft.Framework.Configuration.FunctionalTests/ArrayTests.cs +++ b/test/Microsoft.Framework.Configuration.FunctionalTests/ArrayTests.cs @@ -62,14 +62,23 @@ public void DifferentConfigSources_Merged_KeysAreSorted() var indexConfigurationSections = configurationSection.GetChildren().ToArray(); Assert.Equal(8, indexConfigurationSections.Length); - Assert.Equal("address:0", indexConfigurationSections[0].Key); - Assert.Equal("address:1", indexConfigurationSections[1].Key); - Assert.Equal("address:2", indexConfigurationSections[2].Key); - Assert.Equal("address:3", indexConfigurationSections[3].Key); - Assert.Equal("address:4", indexConfigurationSections[4].Key); - Assert.Equal("address:i", indexConfigurationSections[5].Key); - Assert.Equal("address:j", indexConfigurationSections[6].Key); - Assert.Equal("address:x", indexConfigurationSections[7].Key); + Assert.Equal("0", indexConfigurationSections[0].Key); + Assert.Equal("1", indexConfigurationSections[1].Key); + Assert.Equal("2", indexConfigurationSections[2].Key); + Assert.Equal("3", indexConfigurationSections[3].Key); + Assert.Equal("4", indexConfigurationSections[4].Key); + Assert.Equal("i", indexConfigurationSections[5].Key); + Assert.Equal("j", indexConfigurationSections[6].Key); + Assert.Equal("x", indexConfigurationSections[7].Key); + + Assert.Equal("address:0", indexConfigurationSections[0].Path); + Assert.Equal("address:1", indexConfigurationSections[1].Path); + Assert.Equal("address:2", indexConfigurationSections[2].Path); + Assert.Equal("address:3", indexConfigurationSections[3].Path); + Assert.Equal("address:4", indexConfigurationSections[4].Path); + Assert.Equal("address:i", indexConfigurationSections[5].Path); + Assert.Equal("address:j", indexConfigurationSections[6].Path); + Assert.Equal("address:x", indexConfigurationSections[7].Path); } [Fact] diff --git a/test/Microsoft.Framework.Configuration.Json.Test/ArrayTest.cs b/test/Microsoft.Framework.Configuration.Json.Test/ArrayTest.cs index 9a808dbd..2dcce956 100644 --- a/test/Microsoft.Framework.Configuration.Json.Test/ArrayTest.cs +++ b/test/Microsoft.Framework.Configuration.Json.Test/ArrayTest.cs @@ -233,12 +233,12 @@ public void PropertiesAreSortedByNumberOnlyFirst() var indexConfigurationSections = configurationSection.GetChildren().ToArray(); Assert.Equal(6, indexConfigurationSections.Count()); - Assert.Equal("setting:4", indexConfigurationSections[0].Key); - Assert.Equal("setting:10", indexConfigurationSections[1].Key); - Assert.Equal("setting:42", indexConfigurationSections[2].Key); - Assert.Equal("setting:1text", indexConfigurationSections[3].Key); - Assert.Equal("setting:bob", indexConfigurationSections[4].Key); - Assert.Equal("setting:hello", indexConfigurationSections[5].Key); + Assert.Equal("4", indexConfigurationSections[0].Key); + Assert.Equal("10", indexConfigurationSections[1].Key); + Assert.Equal("42", indexConfigurationSections[2].Key); + Assert.Equal("1text", indexConfigurationSections[3].Key); + Assert.Equal("bob", indexConfigurationSections[4].Key); + Assert.Equal("hello", indexConfigurationSections[5].Key); } } } diff --git a/test/Microsoft.Framework.Configuration.Test/ConfigurationTest.cs b/test/Microsoft.Framework.Configuration.Test/ConfigurationTest.cs index d1a98483..9c9db794 100644 --- a/test/Microsoft.Framework.Configuration.Test/ConfigurationTest.cs +++ b/test/Microsoft.Framework.Configuration.Test/ConfigurationTest.cs @@ -189,7 +189,7 @@ public void CanGetConfigurationSection() } [Fact] - public void CanGetConfigurationSections() + public void CanGetConfigurationChildren() { // Arrange var dic1 = new Dictionary() @@ -221,11 +221,11 @@ public void CanGetConfigurationSections() // Assert Assert.Equal(2, configSections.Count()); - Assert.Equal("MemVal1", configSections.FirstOrDefault(c => c.Key == "Data:DB1")["Connection1"]); - Assert.Equal("MemVal2", configSections.FirstOrDefault(c => c.Key == "Data:DB1")["Connection2"]); - Assert.Equal("MemVal3", configSections.FirstOrDefault(c => c.Key == "Data:DB2Connection").Value); - Assert.False(configSections.Exists(c => c.Key == "Data:DB3")); - Assert.False(configSections.Exists(c => c.Key == "Source:DB3")); + 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]