From 0d634f5652563a193f08d4c02e565d429c1b8592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Fri, 24 Feb 2017 02:00:43 +0100 Subject: [PATCH 1/8] NH-3952: non-regression test cases --- .../NHSpecificTest/NH3952/Entity.cs | 14 ++++ .../NHSpecificTest/NH3952/Fixture.cs | 81 +++++++++++++++++++ .../NHSpecificTest/NH3952/Mappings.hbm.xml | 21 +++++ src/NHibernate.Test/NHibernate.Test.csproj | 3 + 4 files changed, 119 insertions(+) create mode 100644 src/NHibernate.Test/NHSpecificTest/NH3952/Entity.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/NH3952/Fixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/NH3952/Mappings.hbm.xml diff --git a/src/NHibernate.Test/NHSpecificTest/NH3952/Entity.cs b/src/NHibernate.Test/NHSpecificTest/NH3952/Entity.cs new file mode 100644 index 00000000000..caf5e80cdd4 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3952/Entity.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace NHibernate.Test.NHSpecificTest.NH3952 +{ + class Entity + { + public virtual Guid Id { get; set; } + public virtual string Name { get; set; } + public virtual Guid? ParentId { get; set; } + public virtual ISet Children { get; set; } + public virtual string[] Hobbies { get; set; } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/NHSpecificTest/NH3952/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3952/Fixture.cs new file mode 100644 index 00000000000..60efea760aa --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3952/Fixture.cs @@ -0,0 +1,81 @@ +using System.Linq; +using NHibernate.Linq; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.NH3952 +{ + [TestFixture] + public class Fixture : BugTestCase + { + protected override void OnSetUp() + { + using (ISession session = OpenSession()) + using (ITransaction transaction = session.BeginTransaction()) + { + var e1 = new Entity { Name = "Bob" }; + session.Save(e1); + + var e2 = new Entity { Name = "Sally", ParentId = e1.Id, Hobbies = new[] { "Inline skate", "Sailing" } }; + session.Save(e2); + + var e3 = new Entity { Name = "Max", ParentId = e1.Id }; + session.Save(e3); + + session.Flush(); + transaction.Commit(); + } + } + + protected override void OnTearDown() + { + using (ISession session = OpenSession()) + using (ITransaction transaction = session.BeginTransaction()) + { + session.Delete("from System.Object"); + + session.Flush(); + transaction.Commit(); + } + } + + [Test] + public void SimpleNestedSelect() + { + using (ISession session = OpenSession()) + using (session.BeginTransaction()) + { + var result = session.Query() + .Select(e => new { e.Name, children = e.Children.Select(c => c.Name).ToArray() }) + .OrderBy(e => e.Name) + .ToList(); + + Assert.AreEqual(3, result.Count); + Assert.AreEqual(2, result[0].children.Length); + Assert.AreEqual("Bob", result[0].Name); + Assert.Contains("Max", result[0].children); + Assert.Contains("Sally", result[0].children); + Assert.AreEqual(0, result[1].children.Length + result[2].children.Length); + } + } + + [Test] + public void ArraySelect() + { + using (ISession session = OpenSession()) + using (session.BeginTransaction()) + { + var result = session.Query() + .Select(e => new { e.Name, e.Hobbies }) + .OrderBy(e => e.Name) + .ToList(); + + Assert.AreEqual(3, result.Count); + Assert.AreEqual(2, result[2].Hobbies.Length); + Assert.AreEqual("Sally", result[2].Name); + Assert.Contains("Inline skate", result[2].Hobbies); + Assert.Contains("Sailing", result[2].Hobbies); + Assert.AreEqual(0, result[0].Hobbies.Length + result[1].Hobbies.Length); + } + } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/NHSpecificTest/NH3952/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/NH3952/Mappings.hbm.xml new file mode 100644 index 00000000000..6f23678f327 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/NH3952/Mappings.hbm.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/NHibernate.Test/NHibernate.Test.csproj b/src/NHibernate.Test/NHibernate.Test.csproj index f21597364cf..3b64847ed1b 100644 --- a/src/NHibernate.Test/NHibernate.Test.csproj +++ b/src/NHibernate.Test/NHibernate.Test.csproj @@ -736,6 +736,8 @@ + + @@ -3201,6 +3203,7 @@ + From b79fff950f9d000d7c7c75c06d3b32dc6e0633ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Fri, 24 Feb 2017 02:25:13 +0100 Subject: [PATCH 2/8] NH-3952: NestedSelectRewriter currated. --- .../NestedSelects/NestedSelectRewriter.cs | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs b/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs index 4358d7b2cae..8ce3e70e6f3 100644 --- a/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs +++ b/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs @@ -16,7 +16,18 @@ namespace NHibernate.Linq.NestedSelects { static class NestedSelectRewriter { - private static readonly MethodInfo CastMethod = EnumerableHelper.GetMethod("Cast", new[] { typeof (IEnumerable) }, new[] { typeof (object[]) }); + private static readonly MethodInfo CastMethod = ReflectionHelper.GetMethod( + () => Enumerable.Cast(null)); + private static readonly MethodInfo GroupByMethod = ReflectionHelper.GetMethod( + () => Enumerable.GroupBy(null, null, (Func)null)); + private static readonly MethodInfo WhereMethod = ReflectionHelper.GetMethod( + () => Enumerable.Where(null, (Func)null)); + private static readonly MethodInfo SelectMethodDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.Select(null, (Func)null)); + private static readonly MethodInfo ToArrayMethodDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.ToArray(null)); + private static readonly MethodInfo ToListMethodDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.ToList(null)); public static void ReWrite(QueryModel queryModel, ISessionFactory sessionFactory) { @@ -51,15 +62,11 @@ public static void ReWrite(QueryModel queryModel, ISessionFactory sessionFactory var keySelector = CreateSelector(elementExpression, 0); var elementSelector = CreateSelector(elementExpression, 1); - - var groupBy = EnumerableHelper.GetMethod("GroupBy", - new[] { typeof (IEnumerable<>), typeof (Func<,>), typeof (Func<,>) }, - new[] { typeof (object[]), typeof (Tuple), typeof (Tuple) }); - + var input = Expression.Parameter(typeof (IEnumerable), "input"); var lambda = Expression.Lambda( - Expression.Call(groupBy, + Expression.Call(GroupByMethod, Expression.Call(CastMethod, input), keySelector, elementSelector), @@ -154,24 +161,16 @@ private static LambdaExpression MakeSelector(ICollection eleme private static Expression SubCollectionQuery(System.Type collectionType, System.Type elementType, Expression source, Expression predicate, Expression selector) { // source.Where(predicate).Select(selector).ToList(); - var whereMethod = EnumerableHelper.GetMethod("Where", - new[] { typeof (IEnumerable<>), typeof (Func<,>) }, - new[] { typeof (Tuple) }); - - var selectMethod = EnumerableHelper.GetMethod("Select", - new[] { typeof (IEnumerable<>), typeof (Func<,>) }, - new[] { typeof (Tuple), elementType }); + var selectMethod = SelectMethodDefinition.MakeGenericMethod(new[] { typeof(Tuple), elementType }); var select = Expression.Call(selectMethod, - Expression.Call(whereMethod, source, predicate), + Expression.Call(WhereMethod, source, predicate), selector); if (collectionType.IsArray) { - var toArrayMethod = EnumerableHelper.GetMethod("ToArray", - new[] { typeof(IEnumerable<>) }, - new[] { elementType }); + var toArrayMethod = ToArrayMethodDefinition.MakeGenericMethod(new[] { elementType }); var array = Expression.Call(toArrayMethod, @select); return array; @@ -181,9 +180,7 @@ private static Expression SubCollectionQuery(System.Type collectionType, System. if (constructor != null) return Expression.New(constructor, (Expression) @select); - var toListMethod = EnumerableHelper.GetMethod("ToList", - new[] { typeof (IEnumerable<>) }, - new[] { elementType }); + var toListMethod = ToListMethodDefinition.MakeGenericMethod(new[] { elementType }); return Expression.Call(Expression.Call(toListMethod, @select), "AsReadonly", From c385ebe3fb1628dbda31b49b9880f290abb79055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Fri, 24 Feb 2017 17:26:19 +0100 Subject: [PATCH 3/8] NH-3952: Added a quick&dirty performance test for deciding to which reflection pattern to stick with this refactoring. --- .../NHSpecificTest/NH3952/Fixture.cs | 71 ++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/src/NHibernate.Test/NHSpecificTest/NH3952/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3952/Fixture.cs index 60efea760aa..3ad67ffb650 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3952/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3952/Fixture.cs @@ -1,4 +1,7 @@ -using System.Linq; +using System.Collections; +using System.Diagnostics; +using System.Linq; +using System.Reflection; using NHibernate.Linq; using NUnit.Framework; @@ -77,5 +80,71 @@ public void ArraySelect() Assert.AreEqual(0, result[0].Hobbies.Length + result[1].Hobbies.Length); } } + + private static readonly MethodInfo CastMethodDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.Cast(null)); + + private static readonly MethodInfo CastMethod = ReflectionHelper.GetMethod( + () => Enumerable.Cast(null)); + + [Test, Explicit("Just a blunt perf comparison among some reflection patterns used in NH")] + public void ReflectionBluntPerfCompare() + { + var swCached = new Stopwatch(); + swCached.Start(); + for (var i = 0; i < 1000; i++) + { + Trace.TraceInformation(CastMethod.ToString()); + } + swCached.Stop(); + + var swCachedDef = new Stopwatch(); + swCachedDef.Start(); + for (var i = 0; i < 1000; i++) + { + var cast = CastMethodDefinition.MakeGenericMethod(new[] { typeof(int) }); + Trace.TraceInformation(cast.ToString()); + } + swCachedDef.Stop(); + + var swRefl = new Stopwatch(); + swRefl.Start(); + for (var i = 0; i < 1000; i++) + { + var cast = ReflectionHelper.GetMethod(() => Enumerable.Cast(null)); + Trace.TraceInformation(cast.ToString()); + } + swRefl.Stop(); + + var swReflDef = new Stopwatch(); + swReflDef.Start(); + for (var i = 0; i < 1000; i++) + { + var cast = ReflectionHelper.GetMethodDefinition(() => Enumerable.Cast(null)) + .MakeGenericMethod(new[] { typeof(int) }); + Trace.TraceInformation(cast.ToString()); + } + swReflDef.Stop(); + + var swEnHlp = new Stopwatch(); + swEnHlp.Start(); + for (var i = 0; i < 1000; i++) + { + // Testing the obsolete helper perf. Disable obsolete warning. Remove this swEnHlp part of the test if this helper is to be removed. +#pragma warning disable 0618 + var cast = EnumerableHelper.GetMethod("Cast", new[] { typeof(IEnumerable) }, new[] { typeof(int) }); +#pragma warning restore 0618 + Trace.TraceInformation(cast.ToString()); + } + swEnHlp.Stop(); + + Assert.Pass(@"Blunt perf timings: +Cached method: {0} +Cached method definition + make gen: {1} +ReflectionHelper.GetMethod: {2} +ReflectionHelper.GetMethodDefinition + make gen: {3} +EnumerableHelper.GetMethod(generic overload): {4}", + swCached.Elapsed, swCachedDef.Elapsed, swRefl.Elapsed, swReflDef.Elapsed, swEnHlp.Elapsed); + } } } \ No newline at end of file From 2b496e8820974a1d60c60cf7ed8a5b2c93bbb267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Fri, 24 Feb 2017 17:59:58 +0100 Subject: [PATCH 4/8] NH-3952: usages of EnumerableHelper purged. --- src/NHibernate/Linq/EnumerableHelper.cs | 4 +- .../ProcessAggregate.cs | 21 ++++++---- .../ProcessAggregateFromSeed.cs | 39 ++++++++++--------- .../ProcessClientSideSelect.cs | 11 +++++- .../ProcessNonAggregatingGroupBy.cs | 17 +++++--- 5 files changed, 57 insertions(+), 35 deletions(-) diff --git a/src/NHibernate/Linq/EnumerableHelper.cs b/src/NHibernate/Linq/EnumerableHelper.cs index 405c6fb2c99..d3bf9999c8d 100644 --- a/src/NHibernate/Linq/EnumerableHelper.cs +++ b/src/NHibernate/Linq/EnumerableHelper.cs @@ -93,8 +93,8 @@ internal static System.Type GetPropertyOrFieldType(this MemberInfo memberInfo) return null; } } - - // TODO rename / remove - reflection helper above is better + + [Obsolete("ReflectionHelper is better")] public static class EnumerableHelper { public static MethodInfo GetMethod(string name, System.Type[] parameterTypes) diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregate.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregate.cs index c4cc369afa8..f8cfde949ce 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregate.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregate.cs @@ -2,16 +2,22 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Reflection; using Remotion.Linq.Clauses.ResultOperators; using Remotion.Linq.Clauses.StreamedData; using Remotion.Linq.Parsing.ExpressionTreeVisitors; namespace NHibernate.Linq.Visitors.ResultOperatorProcessors { - public class ProcessAggregate : IResultOperatorProcessor - { - public void Process(AggregateResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) - { + public class ProcessAggregate : IResultOperatorProcessor + { + private static readonly MethodInfo CastMethodDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.Cast(null)); + private static readonly MethodInfo AggregateMethodDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.Aggregate(null, null)); + + public void Process(AggregateResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) + { var inputExpr = ((StreamedSequenceInfo)queryModelVisitor.PreviousEvaluationType).ItemExpression; var inputType = inputExpr.Type; var paramExpr = Expression.Parameter(inputType, "item"); @@ -22,11 +28,10 @@ public void Process(AggregateResultOperator resultOperator, QueryModelVisitor qu var inputList = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(typeof(object)), "inputList"); - var castToItem = EnumerableHelper.GetMethod("Cast", new[] { typeof(IEnumerable) }, new[] { inputType }); + var castToItem = CastMethodDefinition.MakeGenericMethod(new[] { inputType }); var castToItemExpr = Expression.Call(castToItem, inputList); - var aggregate = ReflectionHelper.GetMethodDefinition(() => Enumerable.Aggregate(null, null)); - aggregate = aggregate.GetGenericMethodDefinition().MakeGenericMethod(inputType); + var aggregate = AggregateMethodDefinition.MakeGenericMethod(inputType); MethodCallExpression call = Expression.Call( aggregate, @@ -36,5 +41,5 @@ public void Process(AggregateResultOperator resultOperator, QueryModelVisitor qu tree.AddListTransformer(Expression.Lambda(call, inputList)); } - } + } } \ No newline at end of file diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregateFromSeed.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregateFromSeed.cs index e3dd5fc7a0c..162a2e8189d 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregateFromSeed.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregateFromSeed.cs @@ -1,40 +1,44 @@ -using System; -using System.Collections; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Text; -using Remotion.Linq.Clauses.ExpressionTreeVisitors; +using System.Reflection; using Remotion.Linq.Clauses.ResultOperators; using Remotion.Linq.Clauses.StreamedData; using Remotion.Linq.Parsing.ExpressionTreeVisitors; namespace NHibernate.Linq.Visitors.ResultOperatorProcessors { - public class ProcessAggregateFromSeed : IResultOperatorProcessor - { - public void Process(AggregateFromSeedResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) - { - var inputExpr = ((StreamedSequenceInfo) queryModelVisitor.PreviousEvaluationType).ItemExpression; + public class ProcessAggregateFromSeed : IResultOperatorProcessor + { + private static readonly MethodInfo CastMethodDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.Cast(null)); + private static readonly MethodInfo AggregateMethodDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.Aggregate(null, null, null)); + private static readonly MethodInfo AggregateWithResultOpMethodDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.Aggregate(null, null, null, null)); + + public void Process(AggregateFromSeedResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) + { + var inputExpr = ((StreamedSequenceInfo)queryModelVisitor.PreviousEvaluationType).ItemExpression; var inputType = inputExpr.Type; - var paramExpr = Expression.Parameter(inputType, "item"); - var accumulatorFunc = Expression.Lambda( + var paramExpr = Expression.Parameter(inputType, "item"); + var accumulatorFunc = Expression.Lambda( ReplacingExpressionTreeVisitor.Replace(inputExpr, paramExpr, resultOperator.Func.Body), resultOperator.Func.Parameters[0], paramExpr); - + var accumulatorType = resultOperator.Func.Parameters[0].Type; var inputList = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(typeof(object)), "inputList"); - var castToItem = EnumerableHelper.GetMethod("Cast", new[] { typeof(IEnumerable) }, new[] { inputType }); + var castToItem = CastMethodDefinition.MakeGenericMethod(new[] { inputType }); var castToItemExpr = Expression.Call(castToItem, inputList); MethodCallExpression call; if (resultOperator.OptionalResultSelector == null) { - var aggregate = ReflectionHelper.GetMethodDefinition(() => Enumerable.Aggregate(null, null, null)); - aggregate = aggregate.GetGenericMethodDefinition().MakeGenericMethod(inputType, accumulatorType); + var aggregate = AggregateMethodDefinition.MakeGenericMethod(inputType, accumulatorType); call = Expression.Call( aggregate, @@ -46,8 +50,7 @@ public void Process(AggregateFromSeedResultOperator resultOperator, QueryModelVi else { var selectorType = resultOperator.OptionalResultSelector.Type.GetGenericArguments()[2]; - var aggregate = ReflectionHelper.GetMethodDefinition(() => Enumerable.Aggregate(null, null, null, null)); - aggregate = aggregate.GetGenericMethodDefinition().MakeGenericMethod(inputType, accumulatorType, selectorType); + var aggregate = AggregateWithResultOpMethodDefinition.MakeGenericMethod(inputType, accumulatorType, selectorType); call = Expression.Call( aggregate, @@ -60,5 +63,5 @@ public void Process(AggregateFromSeedResultOperator resultOperator, QueryModelVi tree.AddListTransformer(Expression.Lambda(call, inputList)); } - } + } } diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessClientSideSelect.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessClientSideSelect.cs index 44dbacc577a..685d076b5a6 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessClientSideSelect.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessClientSideSelect.cs @@ -1,12 +1,19 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; +using System.Reflection; using NHibernate.Linq.GroupBy; namespace NHibernate.Linq.Visitors.ResultOperatorProcessors { public class ProcessClientSideSelect : IResultOperatorProcessor { + private static readonly MethodInfo SelectMethodDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.Select(null, (Func)null)); + private static readonly MethodInfo ToListMethodDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.ToList(null)); + public void Process(ClientSideSelect resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) { var inputType = resultOperator.SelectClause.Parameters[0].Type; @@ -14,8 +21,8 @@ public void Process(ClientSideSelect resultOperator, QueryModelVisitor queryMode var inputList = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(inputType), "inputList"); - var selectMethod = EnumerableHelper.GetMethod("Select", new[] { typeof(IEnumerable<>), typeof(Func<,>) }, new[] { inputType, outputType }); - var toListMethod = EnumerableHelper.GetMethod("ToList", new[] { typeof(IEnumerable<>) }, new[] { outputType }); + var selectMethod = SelectMethodDefinition.MakeGenericMethod(new[] { inputType, outputType }); + var toListMethod = ToListMethodDefinition.MakeGenericMethod(new[] { outputType }); var lambda = Expression.Lambda( Expression.Call(toListMethod, diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessNonAggregatingGroupBy.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessNonAggregatingGroupBy.cs index cd21dc08d30..8a981f9cd61 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessNonAggregatingGroupBy.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessNonAggregatingGroupBy.cs @@ -1,7 +1,9 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; +using System.Reflection; using NHibernate.Linq.ResultOperators; using Remotion.Linq.Clauses.ExpressionTreeVisitors; @@ -9,6 +11,13 @@ namespace NHibernate.Linq.Visitors.ResultOperatorProcessors { public class ProcessNonAggregatingGroupBy : IResultOperatorProcessor { + private static readonly MethodInfo CastMethodDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.Cast(null)); + private static readonly MethodInfo GroupByMethodDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.GroupBy(null, null, (Func)null)); + private static readonly MethodInfo ToListMethodDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.ToList(null)); + public void Process(NonAggregatingGroupBy resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) { var selector = queryModelVisitor.Model.SelectClause.Selector; @@ -26,13 +35,11 @@ public void Process(NonAggregatingGroupBy resultOperator, QueryModelVisitor quer var elementSelectorExpr = ReverseResolvingExpressionTreeVisitor.ReverseResolve(selector, elementSelector); - var groupByMethod = EnumerableHelper.GetMethod("GroupBy", - new[] { typeof(IEnumerable<>), typeof(Func<,>), typeof(Func<,>) }, - new[] { sourceType, keyType, elementType }); + var groupByMethod = GroupByMethodDefinition.MakeGenericMethod(new[] { sourceType, keyType, elementType }); - var castToItem = EnumerableHelper.GetMethod("Cast", new[] { typeof(IEnumerable) }, new[] { sourceType }); + var castToItem = CastMethodDefinition.MakeGenericMethod(new[] { sourceType }); - var toList = EnumerableHelper.GetMethod("ToList", new[] { typeof(IEnumerable<>) }, new[] { resultOperator.GroupBy.ItemType }); + var toList = ToListMethodDefinition.MakeGenericMethod(new[] { resultOperator.GroupBy.ItemType }); Expression castToItemExpr = Expression.Call(castToItem, listParameter); From 2964ad7f6d9adf6443bb30a41511f261defdb3b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Fri, 24 Feb 2017 23:34:41 +0100 Subject: [PATCH 5/8] NH-3952: added a measurement for Hazzik reflection method. --- .../NHSpecificTest/NH3952/Fixture.cs | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/NHibernate.Test/NHSpecificTest/NH3952/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3952/Fixture.cs index 3ad67ffb650..dea02b22cd0 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3952/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3952/Fixture.cs @@ -1,4 +1,6 @@ -using System.Collections; +using System; +using System.Collections; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; @@ -107,6 +109,25 @@ public void ReflectionBluntPerfCompare() } swCachedDef.Stop(); + var swRefl2 = new Stopwatch(); + swRefl2.Start(); + for (var i = 0; i < 1000; i++) + { + var cast = GetMethod2(Enumerable.Cast, (IEnumerable)null); + Trace.TraceInformation(cast.ToString()); + } + swRefl2.Stop(); + + var swRefl2Def = new Stopwatch(); + swRefl2Def.Start(); + for (var i = 0; i < 1000; i++) + { + var cast = GetMethodDefinition2(Enumerable.Cast, (IEnumerable)null) + .MakeGenericMethod(new[] { typeof(int) }); + Trace.TraceInformation(cast.ToString()); + } + swRefl2Def.Stop(); + var swRefl = new Stopwatch(); swRefl.Start(); for (var i = 0; i < 1000; i++) @@ -141,10 +162,24 @@ public void ReflectionBluntPerfCompare() Assert.Pass(@"Blunt perf timings: Cached method: {0} Cached method definition + make gen: {1} +Hazzik GetMethod: {5} +Hazzik GetMethodDefinition + make gen: {6} ReflectionHelper.GetMethod: {2} ReflectionHelper.GetMethodDefinition + make gen: {3} EnumerableHelper.GetMethod(generic overload): {4}", - swCached.Elapsed, swCachedDef.Elapsed, swRefl.Elapsed, swReflDef.Elapsed, swEnHlp.Elapsed); + swCached.Elapsed, swCachedDef.Elapsed, swRefl.Elapsed, swReflDef.Elapsed, swEnHlp.Elapsed, + swRefl2.Elapsed, swRefl2Def.Elapsed); + } + + public static MethodInfo GetMethod2(Func func, T arg1) + { + return func.Method; + } + + public static MethodInfo GetMethodDefinition2(Func func, T arg1) + { + var method = GetMethod2(func, arg1); + return method.IsGenericMethod ? method.GetGenericMethodDefinition() : method; } } } \ No newline at end of file From a8f6ff3683cf4560eb4ef10bdf1328a623183eeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Tue, 28 Feb 2017 16:56:03 +0100 Subject: [PATCH 6/8] NH-3952: better obsolete message. --- src/NHibernate/Linq/EnumerableHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NHibernate/Linq/EnumerableHelper.cs b/src/NHibernate/Linq/EnumerableHelper.cs index d3bf9999c8d..9ccbcc85d08 100644 --- a/src/NHibernate/Linq/EnumerableHelper.cs +++ b/src/NHibernate/Linq/EnumerableHelper.cs @@ -94,7 +94,7 @@ internal static System.Type GetPropertyOrFieldType(this MemberInfo memberInfo) } } - [Obsolete("ReflectionHelper is better")] + [Obsolete("Please use ReflectionHelper instead")] public static class EnumerableHelper { public static MethodInfo GetMethod(string name, System.Type[] parameterTypes) From 7ae87b29104cb07cf21b098394368796096d38e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Tue, 28 Feb 2017 18:40:26 +0100 Subject: [PATCH 7/8] NH-3952: centralized method cache. --- src/NHibernate/Linq/EnumerableHelper.cs | 42 ++++++++++++++++++- .../NestedSelects/NestedSelectRewriter.cs | 13 ++---- .../ProcessAggregate.cs | 12 +----- .../ProcessAggregateFromSeed.cs | 20 +++------ .../ProcessClientSideSelect.cs | 14 ++----- .../ProcessNonAggregatingGroupBy.cs | 17 ++------ 6 files changed, 58 insertions(+), 60 deletions(-) diff --git a/src/NHibernate/Linq/EnumerableHelper.cs b/src/NHibernate/Linq/EnumerableHelper.cs index 9ccbcc85d08..810186087b6 100644 --- a/src/NHibernate/Linq/EnumerableHelper.cs +++ b/src/NHibernate/Linq/EnumerableHelper.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -57,7 +56,7 @@ public static MethodInfo GetMethod(Expression method) if (method == null) throw new ArgumentNullException("method"); - return ((MethodCallExpression) method.Body).Method; + return ((MethodCallExpression)method.Body).Method; } /// @@ -93,6 +92,45 @@ internal static System.Type GetPropertyOrFieldType(this MemberInfo memberInfo) return null; } } + + internal static class ReflectionCache + { + // When adding a method to this cache, please follow the naming convention of those subclasses and fields: + // - Add your method to a subclass named according to the type holding the method, and suffixed with "Methods". + // - Name the field according to the method name. + // - If the method has overloads, suffix it with "With" followed by its parameter names. Do not list parameters + // common to all overloads. + // - If the method is a generic method definition, add "Definition" as final suffix. + // - If the method is generic, suffix it with "On" followed by its generic parameter type names. + // Avoid caching here narrow cases, such as those using specific types and unlikely to be used by many classes. + // Cache them instead in classes using them. + internal static class EnumerableMethods + { + internal static readonly MethodInfo AggregateDefinition = + ReflectionHelper.GetMethodDefinition(() => Enumerable.Aggregate(null, null)); + internal static readonly MethodInfo AggregateWithSeedDefinition = + ReflectionHelper.GetMethodDefinition(() => Enumerable.Aggregate(null, null, null)); + internal static readonly MethodInfo AggregateWithSeedAndResultSelectorDefinition = + ReflectionHelper.GetMethodDefinition(() => Enumerable.Aggregate(null, null, null, null)); + + internal static readonly MethodInfo CastDefinition = + ReflectionHelper.GetMethodDefinition(() => Enumerable.Cast(null)); + internal static readonly MethodInfo CastOnObjectArray = + ReflectionHelper.GetMethod(() => Enumerable.Cast(null)); + + internal static readonly MethodInfo GroupByWithElementSelectorDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.GroupBy(null, null, (Func)null)); + + internal static readonly MethodInfo SelectDefinition = + ReflectionHelper.GetMethodDefinition(() => Enumerable.Select(null, (Func)null)); + + internal static readonly MethodInfo ToArrayDefinition = + ReflectionHelper.GetMethodDefinition(() => Enumerable.ToArray(null)); + + internal static readonly MethodInfo ToListDefinition = + ReflectionHelper.GetMethodDefinition(() => Enumerable.ToList(null)); + } + } [Obsolete("Please use ReflectionHelper instead")] public static class EnumerableHelper diff --git a/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs b/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs index 8ce3e70e6f3..4f26a927518 100644 --- a/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs +++ b/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -16,18 +15,12 @@ namespace NHibernate.Linq.NestedSelects { static class NestedSelectRewriter { - private static readonly MethodInfo CastMethod = ReflectionHelper.GetMethod( - () => Enumerable.Cast(null)); private static readonly MethodInfo GroupByMethod = ReflectionHelper.GetMethod( () => Enumerable.GroupBy(null, null, (Func)null)); private static readonly MethodInfo WhereMethod = ReflectionHelper.GetMethod( () => Enumerable.Where(null, (Func)null)); private static readonly MethodInfo SelectMethodDefinition = ReflectionHelper.GetMethodDefinition( () => Enumerable.Select(null, (Func)null)); - private static readonly MethodInfo ToArrayMethodDefinition = ReflectionHelper.GetMethodDefinition( - () => Enumerable.ToArray(null)); - private static readonly MethodInfo ToListMethodDefinition = ReflectionHelper.GetMethodDefinition( - () => Enumerable.ToList(null)); public static void ReWrite(QueryModel queryModel, ISessionFactory sessionFactory) { @@ -67,7 +60,7 @@ public static void ReWrite(QueryModel queryModel, ISessionFactory sessionFactory var lambda = Expression.Lambda( Expression.Call(GroupByMethod, - Expression.Call(CastMethod, input), + Expression.Call(ReflectionCache.EnumerableMethods.CastOnObjectArray, input), keySelector, elementSelector), input); @@ -170,7 +163,7 @@ private static Expression SubCollectionQuery(System.Type collectionType, System. if (collectionType.IsArray) { - var toArrayMethod = ToArrayMethodDefinition.MakeGenericMethod(new[] { elementType }); + var toArrayMethod = ReflectionCache.EnumerableMethods.ToArrayDefinition.MakeGenericMethod(new[] { elementType }); var array = Expression.Call(toArrayMethod, @select); return array; @@ -180,7 +173,7 @@ private static Expression SubCollectionQuery(System.Type collectionType, System. if (constructor != null) return Expression.New(constructor, (Expression) @select); - var toListMethod = ToListMethodDefinition.MakeGenericMethod(new[] { elementType }); + var toListMethod = ReflectionCache.EnumerableMethods.ToListDefinition.MakeGenericMethod(new[] { elementType }); return Expression.Call(Expression.Call(toListMethod, @select), "AsReadonly", diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregate.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregate.cs index f8cfde949ce..a3ddc22617f 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregate.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregate.cs @@ -1,8 +1,5 @@ -using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; -using System.Reflection; using Remotion.Linq.Clauses.ResultOperators; using Remotion.Linq.Clauses.StreamedData; using Remotion.Linq.Parsing.ExpressionTreeVisitors; @@ -11,11 +8,6 @@ namespace NHibernate.Linq.Visitors.ResultOperatorProcessors { public class ProcessAggregate : IResultOperatorProcessor { - private static readonly MethodInfo CastMethodDefinition = ReflectionHelper.GetMethodDefinition( - () => Enumerable.Cast(null)); - private static readonly MethodInfo AggregateMethodDefinition = ReflectionHelper.GetMethodDefinition( - () => Enumerable.Aggregate(null, null)); - public void Process(AggregateResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) { var inputExpr = ((StreamedSequenceInfo)queryModelVisitor.PreviousEvaluationType).ItemExpression; @@ -28,10 +20,10 @@ public void Process(AggregateResultOperator resultOperator, QueryModelVisitor qu var inputList = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(typeof(object)), "inputList"); - var castToItem = CastMethodDefinition.MakeGenericMethod(new[] { inputType }); + var castToItem = ReflectionCache.EnumerableMethods.CastDefinition.MakeGenericMethod(new[] { inputType }); var castToItemExpr = Expression.Call(castToItem, inputList); - var aggregate = AggregateMethodDefinition.MakeGenericMethod(inputType); + var aggregate = ReflectionCache.EnumerableMethods.AggregateDefinition.MakeGenericMethod(inputType); MethodCallExpression call = Expression.Call( aggregate, diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregateFromSeed.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregateFromSeed.cs index 162a2e8189d..12cbe8fce29 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregateFromSeed.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregateFromSeed.cs @@ -1,8 +1,5 @@ -using System.Collections; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using System.Linq.Expressions; -using System.Reflection; using Remotion.Linq.Clauses.ResultOperators; using Remotion.Linq.Clauses.StreamedData; using Remotion.Linq.Parsing.ExpressionTreeVisitors; @@ -11,13 +8,6 @@ namespace NHibernate.Linq.Visitors.ResultOperatorProcessors { public class ProcessAggregateFromSeed : IResultOperatorProcessor { - private static readonly MethodInfo CastMethodDefinition = ReflectionHelper.GetMethodDefinition( - () => Enumerable.Cast(null)); - private static readonly MethodInfo AggregateMethodDefinition = ReflectionHelper.GetMethodDefinition( - () => Enumerable.Aggregate(null, null, null)); - private static readonly MethodInfo AggregateWithResultOpMethodDefinition = ReflectionHelper.GetMethodDefinition( - () => Enumerable.Aggregate(null, null, null, null)); - public void Process(AggregateFromSeedResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) { var inputExpr = ((StreamedSequenceInfo)queryModelVisitor.PreviousEvaluationType).ItemExpression; @@ -31,14 +21,15 @@ public void Process(AggregateFromSeedResultOperator resultOperator, QueryModelVi var accumulatorType = resultOperator.Func.Parameters[0].Type; var inputList = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(typeof(object)), "inputList"); - var castToItem = CastMethodDefinition.MakeGenericMethod(new[] { inputType }); + var castToItem = ReflectionCache.EnumerableMethods.CastDefinition.MakeGenericMethod(new[] { inputType }); var castToItemExpr = Expression.Call(castToItem, inputList); MethodCallExpression call; if (resultOperator.OptionalResultSelector == null) { - var aggregate = AggregateMethodDefinition.MakeGenericMethod(inputType, accumulatorType); + var aggregate = ReflectionCache.EnumerableMethods.AggregateWithSeedDefinition + .MakeGenericMethod(inputType, accumulatorType); call = Expression.Call( aggregate, @@ -50,7 +41,8 @@ public void Process(AggregateFromSeedResultOperator resultOperator, QueryModelVi else { var selectorType = resultOperator.OptionalResultSelector.Type.GetGenericArguments()[2]; - var aggregate = AggregateWithResultOpMethodDefinition.MakeGenericMethod(inputType, accumulatorType, selectorType); + var aggregate = ReflectionCache.EnumerableMethods.AggregateWithSeedAndResultSelectorDefinition + .MakeGenericMethod(inputType, accumulatorType, selectorType); call = Expression.Call( aggregate, diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessClientSideSelect.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessClientSideSelect.cs index 685d076b5a6..abaec744cee 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessClientSideSelect.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessClientSideSelect.cs @@ -1,19 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using System.Linq.Expressions; -using System.Reflection; using NHibernate.Linq.GroupBy; namespace NHibernate.Linq.Visitors.ResultOperatorProcessors { public class ProcessClientSideSelect : IResultOperatorProcessor { - private static readonly MethodInfo SelectMethodDefinition = ReflectionHelper.GetMethodDefinition( - () => Enumerable.Select(null, (Func)null)); - private static readonly MethodInfo ToListMethodDefinition = ReflectionHelper.GetMethodDefinition( - () => Enumerable.ToList(null)); - public void Process(ClientSideSelect resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) { var inputType = resultOperator.SelectClause.Parameters[0].Type; @@ -21,8 +13,8 @@ public void Process(ClientSideSelect resultOperator, QueryModelVisitor queryMode var inputList = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(inputType), "inputList"); - var selectMethod = SelectMethodDefinition.MakeGenericMethod(new[] { inputType, outputType }); - var toListMethod = ToListMethodDefinition.MakeGenericMethod(new[] { outputType }); + var selectMethod = ReflectionCache.EnumerableMethods.SelectDefinition.MakeGenericMethod(new[] { inputType, outputType }); + var toListMethod = ReflectionCache.EnumerableMethods.ToListDefinition.MakeGenericMethod(new[] { outputType }); var lambda = Expression.Lambda( Expression.Call(toListMethod, diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessNonAggregatingGroupBy.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessNonAggregatingGroupBy.cs index 8a981f9cd61..69f99ab0a17 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessNonAggregatingGroupBy.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessNonAggregatingGroupBy.cs @@ -1,9 +1,6 @@ using System; -using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; -using System.Reflection; using NHibernate.Linq.ResultOperators; using Remotion.Linq.Clauses.ExpressionTreeVisitors; @@ -11,13 +8,6 @@ namespace NHibernate.Linq.Visitors.ResultOperatorProcessors { public class ProcessNonAggregatingGroupBy : IResultOperatorProcessor { - private static readonly MethodInfo CastMethodDefinition = ReflectionHelper.GetMethodDefinition( - () => Enumerable.Cast(null)); - private static readonly MethodInfo GroupByMethodDefinition = ReflectionHelper.GetMethodDefinition( - () => Enumerable.GroupBy(null, null, (Func)null)); - private static readonly MethodInfo ToListMethodDefinition = ReflectionHelper.GetMethodDefinition( - () => Enumerable.ToList(null)); - public void Process(NonAggregatingGroupBy resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) { var selector = queryModelVisitor.Model.SelectClause.Selector; @@ -35,11 +25,12 @@ public void Process(NonAggregatingGroupBy resultOperator, QueryModelVisitor quer var elementSelectorExpr = ReverseResolvingExpressionTreeVisitor.ReverseResolve(selector, elementSelector); - var groupByMethod = GroupByMethodDefinition.MakeGenericMethod(new[] { sourceType, keyType, elementType }); + var groupByMethod = ReflectionCache.EnumerableMethods.GroupByWithElementSelectorDefinition + .MakeGenericMethod(new[] { sourceType, keyType, elementType }); - var castToItem = CastMethodDefinition.MakeGenericMethod(new[] { sourceType }); + var castToItem = ReflectionCache.EnumerableMethods.CastDefinition.MakeGenericMethod(new[] { sourceType }); - var toList = ToListMethodDefinition.MakeGenericMethod(new[] { resultOperator.GroupBy.ItemType }); + var toList = ReflectionCache.EnumerableMethods.ToListDefinition.MakeGenericMethod(new[] { resultOperator.GroupBy.ItemType }); Expression castToItemExpr = Expression.Call(castToItem, listParameter); From b746a13c8b456f727ffc76af16bed7b3603b4cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Tue, 28 Feb 2017 21:53:09 +0100 Subject: [PATCH 8/8] NH-3952: more cleanup. --- src/NHibernate/Linq/EnumerableHelper.cs | 39 ---------------- .../NestedSelects/NestedSelectRewriter.cs | 8 ++-- .../ProcessAggregate.cs | 1 + .../ProcessAggregateFromSeed.cs | 1 + .../ProcessClientSideSelect.cs | 1 + .../ProcessNonAggregatingGroupBy.cs | 1 + src/NHibernate/NHibernate.csproj | 1 + src/NHibernate/Util/ReflectionCache.cs | 44 +++++++++++++++++++ 8 files changed, 53 insertions(+), 43 deletions(-) create mode 100644 src/NHibernate/Util/ReflectionCache.cs diff --git a/src/NHibernate/Linq/EnumerableHelper.cs b/src/NHibernate/Linq/EnumerableHelper.cs index 810186087b6..f36bf432506 100644 --- a/src/NHibernate/Linq/EnumerableHelper.cs +++ b/src/NHibernate/Linq/EnumerableHelper.cs @@ -92,45 +92,6 @@ internal static System.Type GetPropertyOrFieldType(this MemberInfo memberInfo) return null; } } - - internal static class ReflectionCache - { - // When adding a method to this cache, please follow the naming convention of those subclasses and fields: - // - Add your method to a subclass named according to the type holding the method, and suffixed with "Methods". - // - Name the field according to the method name. - // - If the method has overloads, suffix it with "With" followed by its parameter names. Do not list parameters - // common to all overloads. - // - If the method is a generic method definition, add "Definition" as final suffix. - // - If the method is generic, suffix it with "On" followed by its generic parameter type names. - // Avoid caching here narrow cases, such as those using specific types and unlikely to be used by many classes. - // Cache them instead in classes using them. - internal static class EnumerableMethods - { - internal static readonly MethodInfo AggregateDefinition = - ReflectionHelper.GetMethodDefinition(() => Enumerable.Aggregate(null, null)); - internal static readonly MethodInfo AggregateWithSeedDefinition = - ReflectionHelper.GetMethodDefinition(() => Enumerable.Aggregate(null, null, null)); - internal static readonly MethodInfo AggregateWithSeedAndResultSelectorDefinition = - ReflectionHelper.GetMethodDefinition(() => Enumerable.Aggregate(null, null, null, null)); - - internal static readonly MethodInfo CastDefinition = - ReflectionHelper.GetMethodDefinition(() => Enumerable.Cast(null)); - internal static readonly MethodInfo CastOnObjectArray = - ReflectionHelper.GetMethod(() => Enumerable.Cast(null)); - - internal static readonly MethodInfo GroupByWithElementSelectorDefinition = ReflectionHelper.GetMethodDefinition( - () => Enumerable.GroupBy(null, null, (Func)null)); - - internal static readonly MethodInfo SelectDefinition = - ReflectionHelper.GetMethodDefinition(() => Enumerable.Select(null, (Func)null)); - - internal static readonly MethodInfo ToArrayDefinition = - ReflectionHelper.GetMethodDefinition(() => Enumerable.ToArray(null)); - - internal static readonly MethodInfo ToListDefinition = - ReflectionHelper.GetMethodDefinition(() => Enumerable.ToList(null)); - } - } [Obsolete("Please use ReflectionHelper instead")] public static class EnumerableHelper diff --git a/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs b/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs index 4f26a927518..56a5ded7921 100644 --- a/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs +++ b/src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs @@ -15,12 +15,12 @@ namespace NHibernate.Linq.NestedSelects { static class NestedSelectRewriter { + private static readonly MethodInfo CastMethod = + ReflectionHelper.GetMethod(() => Enumerable.Cast(null)); private static readonly MethodInfo GroupByMethod = ReflectionHelper.GetMethod( () => Enumerable.GroupBy(null, null, (Func)null)); private static readonly MethodInfo WhereMethod = ReflectionHelper.GetMethod( () => Enumerable.Where(null, (Func)null)); - private static readonly MethodInfo SelectMethodDefinition = ReflectionHelper.GetMethodDefinition( - () => Enumerable.Select(null, (Func)null)); public static void ReWrite(QueryModel queryModel, ISessionFactory sessionFactory) { @@ -60,7 +60,7 @@ public static void ReWrite(QueryModel queryModel, ISessionFactory sessionFactory var lambda = Expression.Lambda( Expression.Call(GroupByMethod, - Expression.Call(ReflectionCache.EnumerableMethods.CastOnObjectArray, input), + Expression.Call(CastMethod, input), keySelector, elementSelector), input); @@ -155,7 +155,7 @@ private static Expression SubCollectionQuery(System.Type collectionType, System. { // source.Where(predicate).Select(selector).ToList(); - var selectMethod = SelectMethodDefinition.MakeGenericMethod(new[] { typeof(Tuple), elementType }); + var selectMethod = ReflectionCache.EnumerableMethods.SelectDefinition.MakeGenericMethod(new[] { typeof(Tuple), elementType }); var select = Expression.Call(selectMethod, Expression.Call(WhereMethod, source, predicate), diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregate.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregate.cs index a3ddc22617f..2a7f9f81995 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregate.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregate.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq.Expressions; +using NHibernate.Util; using Remotion.Linq.Clauses.ResultOperators; using Remotion.Linq.Clauses.StreamedData; using Remotion.Linq.Parsing.ExpressionTreeVisitors; diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregateFromSeed.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregateFromSeed.cs index 12cbe8fce29..34f3b4d39fe 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregateFromSeed.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessAggregateFromSeed.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq.Expressions; +using NHibernate.Util; using Remotion.Linq.Clauses.ResultOperators; using Remotion.Linq.Clauses.StreamedData; using Remotion.Linq.Parsing.ExpressionTreeVisitors; diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessClientSideSelect.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessClientSideSelect.cs index abaec744cee..81bf0fc1c7d 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessClientSideSelect.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessClientSideSelect.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq.Expressions; using NHibernate.Linq.GroupBy; +using NHibernate.Util; namespace NHibernate.Linq.Visitors.ResultOperatorProcessors { diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessNonAggregatingGroupBy.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessNonAggregatingGroupBy.cs index 69f99ab0a17..abaf20e1471 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessNonAggregatingGroupBy.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessNonAggregatingGroupBy.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq.Expressions; using NHibernate.Linq.ResultOperators; +using NHibernate.Util; using Remotion.Linq.Clauses.ExpressionTreeVisitors; namespace NHibernate.Linq.Visitors.ResultOperatorProcessors diff --git a/src/NHibernate/NHibernate.csproj b/src/NHibernate/NHibernate.csproj index 851c1a7fc6d..d850d9107da 100644 --- a/src/NHibernate/NHibernate.csproj +++ b/src/NHibernate/NHibernate.csproj @@ -740,6 +740,7 @@ + diff --git a/src/NHibernate/Util/ReflectionCache.cs b/src/NHibernate/Util/ReflectionCache.cs new file mode 100644 index 00000000000..8cd4a94253c --- /dev/null +++ b/src/NHibernate/Util/ReflectionCache.cs @@ -0,0 +1,44 @@ +using System; +using System.Linq; +using System.Reflection; +using NHibernate.Linq; + +namespace NHibernate.Util +{ + internal static class ReflectionCache + { + // When adding a method to this cache, please follow the naming convention of those subclasses and fields: + // - Add your method to a subclass named according to the type holding the method, and suffixed with "Methods". + // - Name the field according to the method name. + // - If the method has overloads, suffix it with "With" followed by its parameter names. Do not list parameters + // common to all overloads. + // - If the method is a generic method definition, add "Definition" as final suffix. + // - If the method is generic, suffix it with "On" followed by its generic parameter type names. + // Avoid caching here narrow cases, such as those using specific types and unlikely to be used by many classes. + // Cache them instead in classes using them. + internal static class EnumerableMethods + { + internal static readonly MethodInfo AggregateDefinition = + ReflectionHelper.GetMethodDefinition(() => Enumerable.Aggregate(null, null)); + internal static readonly MethodInfo AggregateWithSeedDefinition = + ReflectionHelper.GetMethodDefinition(() => Enumerable.Aggregate(null, null, null)); + internal static readonly MethodInfo AggregateWithSeedAndResultSelectorDefinition = + ReflectionHelper.GetMethodDefinition(() => Enumerable.Aggregate(null, null, null, null)); + + internal static readonly MethodInfo CastDefinition = + ReflectionHelper.GetMethodDefinition(() => Enumerable.Cast(null)); + + internal static readonly MethodInfo GroupByWithElementSelectorDefinition = ReflectionHelper.GetMethodDefinition( + () => Enumerable.GroupBy(null, null, (Func)null)); + + internal static readonly MethodInfo SelectDefinition = + ReflectionHelper.GetMethodDefinition(() => Enumerable.Select(null, (Func)null)); + + internal static readonly MethodInfo ToArrayDefinition = + ReflectionHelper.GetMethodDefinition(() => Enumerable.ToArray(null)); + + internal static readonly MethodInfo ToListDefinition = + ReflectionHelper.GetMethodDefinition(() => Enumerable.ToList(null)); + } + } +} \ No newline at end of file