From cdd8e2cf7b31ce0b25b8e83a3fe3af32072e5b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericDelaporte@users.noreply.github.com> Date: Sat, 6 Oct 2018 17:48:39 +0200 Subject: [PATCH] Replace an O(n) lookup in LINQ query parsing by an O(1) one The HQL generators registry already uses dictionaries to resolve generators for methods found in LINQ expression. But some generators do not declare statically their supported methods and instead have to be queried with a loop on them for support of methods encountered in the LINQ query. The result of this lookup can be cached for avoiding calling this loop again on the next query using the method. If #1868 is merged, this change will compensate for #1868 causing up to three such lookups by method. --- src/NHibernate.Test/Async/Hql/HQLFunctions.cs | 2 +- .../DefaultLinqToHqlGeneratorsRegistry.cs | 24 ++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/NHibernate.Test/Async/Hql/HQLFunctions.cs b/src/NHibernate.Test/Async/Hql/HQLFunctions.cs index d5ea20b1681..e09e5a426ba 100644 --- a/src/NHibernate.Test/Async/Hql/HQLFunctions.cs +++ b/src/NHibernate.Test/Async/Hql/HQLFunctions.cs @@ -1079,9 +1079,9 @@ public async Task Current_DateAsync() public async Task Current_Date_IsLowestTimeOfDayAsync() { AssumeFunctionSupported("current_date"); - var now = DateTime.Now; if (!TestDialect.SupportsNonDataBoundCondition) Assert.Ignore("Test is not supported by the target database"); + var now = DateTime.Now; if (now.TimeOfDay < TimeSpan.FromMinutes(5) || now.TimeOfDay > TimeSpan.Parse("23:55")) Assert.Ignore("Test is unreliable around midnight"); diff --git a/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs b/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs index 2a694a9294a..ea5ab8a159c 100644 --- a/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs +++ b/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -10,6 +11,9 @@ public class DefaultLinqToHqlGeneratorsRegistry : ILinqToHqlGeneratorsRegistry private readonly Dictionary registeredProperties = new Dictionary(); private readonly List runtimeMethodHqlGenerators = new List(); + private readonly ConcurrentDictionary _cachedRuntimeMethodHqlGenerators = + new ConcurrentDictionary(); + public DefaultLinqToHqlGeneratorsRegistry() { RegisterGenerator(new StandardLinqExtensionMethodGenerator()); @@ -69,14 +73,15 @@ public DefaultLinqToHqlGeneratorsRegistry() protected bool GetRuntimeMethodGenerator(MethodInfo method, out IHqlGeneratorForMethod methodGenerator) { - methodGenerator = null; - - foreach (var typeGenerator in runtimeMethodHqlGenerators.Where(typeGenerator => typeGenerator.SupportsMethod(method))) - { - methodGenerator = typeGenerator.GetMethodGenerator(method); - return true; - } - return false; + methodGenerator = _cachedRuntimeMethodHqlGenerators.GetOrAdd( + method, + m => + runtimeMethodHqlGenerators + .Where(g => g.SupportsMethod(m)) + .Select(g => g.GetMethodGenerator(m)) + .FirstOrDefault()); + + return methodGenerator != null; } public virtual bool TryGetGenerator(MethodInfo method, out IHqlGeneratorForMethod generator) @@ -112,6 +117,7 @@ public virtual void RegisterGenerator(MemberInfo property, IHqlGeneratorForPrope public void RegisterGenerator(IRuntimeMethodHqlGenerator generator) { runtimeMethodHqlGenerators.Add(generator); + _cachedRuntimeMethodHqlGenerators.Clear(); } } }