Remove most LINQ usage from Microsoft.Extensions.Configuration (#44825)
authorStephen Toub <stoub@microsoft.com>
Wed, 18 Nov 2020 17:13:45 +0000 (12:13 -0500)
committerGitHub <noreply@github.com>
Wed, 18 Nov 2020 17:13:45 +0000 (12:13 -0500)
Resulting in several hundred Func, IEnumerable, set, and string allocations at startup.

src/libraries/Microsoft.Extensions.Configuration/src/ChainedConfigurationProvider.cs
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationProvider.cs
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationRoot.cs

index 4460595..a240f3d 100644 (file)
@@ -3,7 +3,6 @@
 
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using Microsoft.Extensions.Primitives;
 
 namespace Microsoft.Extensions.Configuration
@@ -78,11 +77,14 @@ namespace Microsoft.Extensions.Configuration
             string parentPath)
         {
             IConfiguration section = parentPath == null ? _config : _config.GetSection(parentPath);
-            IEnumerable<IConfigurationSection> children = section.GetChildren();
             var keys = new List<string>();
-            keys.AddRange(children.Select(c => c.Key));
-            return keys.Concat(earlierKeys)
-                .OrderBy(k => k, ConfigurationKeyComparer.Instance);
+            foreach (IConfigurationSection child in section.GetChildren())
+            {
+                keys.Add(child.Key);
+            }
+            keys.AddRange(earlierKeys);
+            keys.Sort(ConfigurationKeyComparer.Comparison);
+            return keys;
         }
 
         /// <inheritdoc />
index b0abce1..b5dc31f 100644 (file)
@@ -18,6 +18,9 @@ namespace Microsoft.Extensions.Configuration
         /// </summary>
         public static ConfigurationKeyComparer Instance { get; } = new ConfigurationKeyComparer();
 
+        /// <summary>A comparer delegate with the default instance.</summary>
+        internal static Comparison<string> Comparison { get; } = Instance.Compare;
+
         /// <summary>
         /// Compares two strings.
         /// </summary>
index 15927fe..e337bd3 100644 (file)
@@ -3,7 +3,7 @@
 
 using System;
 using System.Collections.Generic;
-using System.Linq;
+using System.Diagnostics;
 using System.Threading;
 using Microsoft.Extensions.Primitives;
 
@@ -62,13 +62,35 @@ namespace Microsoft.Extensions.Configuration
             IEnumerable<string> earlierKeys,
             string parentPath)
         {
-            string prefix = parentPath == null ? string.Empty : parentPath + ConfigurationPath.KeyDelimiter;
+            var results = new List<string>();
 
-            return Data
-                .Where(kv => kv.Key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
-                .Select(kv => Segment(kv.Key, prefix.Length))
-                .Concat(earlierKeys)
-                .OrderBy(k => k, ConfigurationKeyComparer.Instance);
+            if (parentPath is null)
+            {
+                foreach (KeyValuePair<string, string> kv in Data)
+                {
+                    results.Add(Segment(kv.Key, 0));
+                }
+            }
+            else
+            {
+                Debug.Assert(ConfigurationPath.KeyDelimiter == ":");
+
+                foreach (KeyValuePair<string, string> kv in Data)
+                {
+                    if (kv.Key.Length > parentPath.Length &&
+                        kv.Key.StartsWith(parentPath, StringComparison.OrdinalIgnoreCase) &&
+                        kv.Key[parentPath.Length] == ':')
+                    {
+                        results.Add(Segment(kv.Key, parentPath.Length + 1));
+                    }
+                }
+            }
+
+            results.AddRange(earlierKeys);
+
+            results.Sort(ConfigurationKeyComparer.Comparison);
+
+            return results;
         }
 
         private static string Segment(string key, int prefixLength)
index bff21d5..455efdb 100644 (file)
@@ -3,7 +3,6 @@
 
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using System.Threading;
 using Microsoft.Extensions.Primitives;
 
@@ -66,7 +65,7 @@ namespace Microsoft.Extensions.Configuration
             }
             set
             {
-                if (!_providers.Any())
+                if (_providers.Count == 0)
                 {
                     throw new InvalidOperationException(SR.Error_NoSources);
                 }