Skip to content

Commit 585d83e

Browse files
fredericDelaportehazzik
authored andcommitted
NH-3952 - Replace usages of EnumerableHelper with ReflectionHelper (#557)
1 parent fd7959e commit 585d83e

File tree

12 files changed

+316
-66
lines changed

12 files changed

+316
-66
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace NHibernate.Test.NHSpecificTest.NH3952
5+
{
6+
class Entity
7+
{
8+
public virtual Guid Id { get; set; }
9+
public virtual string Name { get; set; }
10+
public virtual Guid? ParentId { get; set; }
11+
public virtual ISet<Entity> Children { get; set; }
12+
public virtual string[] Hobbies { get; set; }
13+
}
14+
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Diagnostics;
5+
using System.Linq;
6+
using System.Reflection;
7+
using NHibernate.Linq;
8+
using NUnit.Framework;
9+
10+
namespace NHibernate.Test.NHSpecificTest.NH3952
11+
{
12+
[TestFixture]
13+
public class Fixture : BugTestCase
14+
{
15+
protected override void OnSetUp()
16+
{
17+
using (ISession session = OpenSession())
18+
using (ITransaction transaction = session.BeginTransaction())
19+
{
20+
var e1 = new Entity { Name = "Bob" };
21+
session.Save(e1);
22+
23+
var e2 = new Entity { Name = "Sally", ParentId = e1.Id, Hobbies = new[] { "Inline skate", "Sailing" } };
24+
session.Save(e2);
25+
26+
var e3 = new Entity { Name = "Max", ParentId = e1.Id };
27+
session.Save(e3);
28+
29+
session.Flush();
30+
transaction.Commit();
31+
}
32+
}
33+
34+
protected override void OnTearDown()
35+
{
36+
using (ISession session = OpenSession())
37+
using (ITransaction transaction = session.BeginTransaction())
38+
{
39+
session.Delete("from System.Object");
40+
41+
session.Flush();
42+
transaction.Commit();
43+
}
44+
}
45+
46+
[Test]
47+
public void SimpleNestedSelect()
48+
{
49+
using (ISession session = OpenSession())
50+
using (session.BeginTransaction())
51+
{
52+
var result = session.Query<Entity>()
53+
.Select(e => new { e.Name, children = e.Children.Select(c => c.Name).ToArray() })
54+
.OrderBy(e => e.Name)
55+
.ToList();
56+
57+
Assert.AreEqual(3, result.Count);
58+
Assert.AreEqual(2, result[0].children.Length);
59+
Assert.AreEqual("Bob", result[0].Name);
60+
Assert.Contains("Max", result[0].children);
61+
Assert.Contains("Sally", result[0].children);
62+
Assert.AreEqual(0, result[1].children.Length + result[2].children.Length);
63+
}
64+
}
65+
66+
[Test]
67+
public void ArraySelect()
68+
{
69+
using (ISession session = OpenSession())
70+
using (session.BeginTransaction())
71+
{
72+
var result = session.Query<Entity>()
73+
.Select(e => new { e.Name, e.Hobbies })
74+
.OrderBy(e => e.Name)
75+
.ToList();
76+
77+
Assert.AreEqual(3, result.Count);
78+
Assert.AreEqual(2, result[2].Hobbies.Length);
79+
Assert.AreEqual("Sally", result[2].Name);
80+
Assert.Contains("Inline skate", result[2].Hobbies);
81+
Assert.Contains("Sailing", result[2].Hobbies);
82+
Assert.AreEqual(0, result[0].Hobbies.Length + result[1].Hobbies.Length);
83+
}
84+
}
85+
86+
private static readonly MethodInfo CastMethodDefinition = ReflectionHelper.GetMethodDefinition(
87+
() => Enumerable.Cast<object>(null));
88+
89+
private static readonly MethodInfo CastMethod = ReflectionHelper.GetMethod(
90+
() => Enumerable.Cast<int>(null));
91+
92+
[Test, Explicit("Just a blunt perf comparison among some reflection patterns used in NH")]
93+
public void ReflectionBluntPerfCompare()
94+
{
95+
var swCached = new Stopwatch();
96+
swCached.Start();
97+
for (var i = 0; i < 1000; i++)
98+
{
99+
Trace.TraceInformation(CastMethod.ToString());
100+
}
101+
swCached.Stop();
102+
103+
var swCachedDef = new Stopwatch();
104+
swCachedDef.Start();
105+
for (var i = 0; i < 1000; i++)
106+
{
107+
var cast = CastMethodDefinition.MakeGenericMethod(new[] { typeof(int) });
108+
Trace.TraceInformation(cast.ToString());
109+
}
110+
swCachedDef.Stop();
111+
112+
var swRefl2 = new Stopwatch();
113+
swRefl2.Start();
114+
for (var i = 0; i < 1000; i++)
115+
{
116+
var cast = GetMethod2(Enumerable.Cast<int>, (IEnumerable<int>)null);
117+
Trace.TraceInformation(cast.ToString());
118+
}
119+
swRefl2.Stop();
120+
121+
var swRefl2Def = new Stopwatch();
122+
swRefl2Def.Start();
123+
for (var i = 0; i < 1000; i++)
124+
{
125+
var cast = GetMethodDefinition2(Enumerable.Cast<object>, (IEnumerable<object>)null)
126+
.MakeGenericMethod(new[] { typeof(int) });
127+
Trace.TraceInformation(cast.ToString());
128+
}
129+
swRefl2Def.Stop();
130+
131+
var swRefl = new Stopwatch();
132+
swRefl.Start();
133+
for (var i = 0; i < 1000; i++)
134+
{
135+
var cast = ReflectionHelper.GetMethod(() => Enumerable.Cast<int>(null));
136+
Trace.TraceInformation(cast.ToString());
137+
}
138+
swRefl.Stop();
139+
140+
var swReflDef = new Stopwatch();
141+
swReflDef.Start();
142+
for (var i = 0; i < 1000; i++)
143+
{
144+
var cast = ReflectionHelper.GetMethodDefinition(() => Enumerable.Cast<object>(null))
145+
.MakeGenericMethod(new[] { typeof(int) });
146+
Trace.TraceInformation(cast.ToString());
147+
}
148+
swReflDef.Stop();
149+
150+
var swEnHlp = new Stopwatch();
151+
swEnHlp.Start();
152+
for (var i = 0; i < 1000; i++)
153+
{
154+
// Testing the obsolete helper perf. Disable obsolete warning. Remove this swEnHlp part of the test if this helper is to be removed.
155+
#pragma warning disable 0618
156+
var cast = EnumerableHelper.GetMethod("Cast", new[] { typeof(IEnumerable) }, new[] { typeof(int) });
157+
#pragma warning restore 0618
158+
Trace.TraceInformation(cast.ToString());
159+
}
160+
swEnHlp.Stop();
161+
162+
Assert.Pass(@"Blunt perf timings:
163+
Cached method: {0}
164+
Cached method definition + make gen: {1}
165+
Hazzik GetMethod: {5}
166+
Hazzik GetMethodDefinition + make gen: {6}
167+
ReflectionHelper.GetMethod: {2}
168+
ReflectionHelper.GetMethodDefinition + make gen: {3}
169+
EnumerableHelper.GetMethod(generic overload): {4}",
170+
swCached.Elapsed, swCachedDef.Elapsed, swRefl.Elapsed, swReflDef.Elapsed, swEnHlp.Elapsed,
171+
swRefl2.Elapsed, swRefl2Def.Elapsed);
172+
}
173+
174+
public static MethodInfo GetMethod2<T, TResult>(Func<T, TResult> func, T arg1)
175+
{
176+
return func.Method;
177+
}
178+
179+
public static MethodInfo GetMethodDefinition2<T, TResult>(Func<T, TResult> func, T arg1)
180+
{
181+
var method = GetMethod2(func, arg1);
182+
return method.IsGenericMethod ? method.GetGenericMethodDefinition() : method;
183+
}
184+
}
185+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test" namespace="NHibernate.Test.NHSpecificTest.NH3952">
3+
4+
<class name="Entity">
5+
<id name="Id" generator="guid.comb" />
6+
<property name="Name" />
7+
<property name="ParentId" />
8+
<set name="Children">
9+
<key column="ParentId"/>
10+
<one-to-many class="Entity" />
11+
</set>
12+
<array name="Hobbies" table="Hobby">
13+
<key column="EntityId" />
14+
<index column="`Index`"/>
15+
<element type="string" length="50">
16+
<column name="Name" />
17+
</element>
18+
</array>
19+
</class>
20+
21+
</hibernate-mapping>

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,8 @@
737737
<Compile Include="NHSpecificTest\EntityWithUserTypeCanHaveLinqGenerators\Fixture.cs" />
738738
<Compile Include="NHSpecificTest\EntityWithUserTypeCanHaveLinqGenerators\FooExample.cs" />
739739
<Compile Include="NHSpecificTest\EntityWithUserTypeCanHaveLinqGenerators\IExample.cs" />
740+
<Compile Include="NHSpecificTest\NH3952\Entity.cs" />
741+
<Compile Include="NHSpecificTest\NH3952\Fixture.cs" />
740742
<Compile Include="NHSpecificTest\NH2204\Model.cs" />
741743
<Compile Include="NHSpecificTest\NH2204\Fixture.cs" />
742744
<Compile Include="NHSpecificTest\NH3912\BatcherLovingEntity.cs" />
@@ -3204,6 +3206,7 @@
32043206
<EmbeddedResource Include="NHSpecificTest\NH1291AnonExample\Mappings.hbm.xml" />
32053207
</ItemGroup>
32063208
<ItemGroup>
3209+
<EmbeddedResource Include="NHSpecificTest\NH3952\Mappings.hbm.xml" />
32073210
<EmbeddedResource Include="NHSpecificTest\NH2204\Mappings.hbm.xml" />
32083211
<EmbeddedResource Include="NHSpecificTest\NH3874\Mappings.hbm.xml" />
32093212
<EmbeddedResource Include="NHSpecificTest\EntityWithUserTypeCanHaveLinqGenerators\Mappings.hbm.xml" />

src/NHibernate/Linq/EnumerableHelper.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections;
32
using System.Linq;
43
using System.Linq.Expressions;
54
using System.Reflection;
@@ -57,7 +56,7 @@ public static MethodInfo GetMethod(Expression<System.Action> method)
5756
if (method == null)
5857
throw new ArgumentNullException("method");
5958

60-
return ((MethodCallExpression) method.Body).Method;
59+
return ((MethodCallExpression)method.Body).Method;
6160
}
6261

6362
/// <summary>
@@ -93,8 +92,8 @@ internal static System.Type GetPropertyOrFieldType(this MemberInfo memberInfo)
9392
return null;
9493
}
9594
}
96-
97-
// TODO rename / remove - reflection helper above is better
95+
96+
[Obsolete("Please use ReflectionHelper instead")]
9897
public static class EnumerableHelper
9998
{
10099
public static MethodInfo GetMethod(string name, System.Type[] parameterTypes)

src/NHibernate/Linq/NestedSelects/NestedSelectRewriter.cs

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections;
32
using System.Collections.Generic;
43
using System.Linq;
54
using System.Linq.Expressions;
@@ -16,7 +15,12 @@ namespace NHibernate.Linq.NestedSelects
1615
{
1716
static class NestedSelectRewriter
1817
{
19-
private static readonly MethodInfo CastMethod = EnumerableHelper.GetMethod("Cast", new[] { typeof (IEnumerable) }, new[] { typeof (object[]) });
18+
private static readonly MethodInfo CastMethod =
19+
ReflectionHelper.GetMethod(() => Enumerable.Cast<object[]>(null));
20+
private static readonly MethodInfo GroupByMethod = ReflectionHelper.GetMethod(
21+
() => Enumerable.GroupBy<object[], Tuple, Tuple>(null, null, (Func<object[], Tuple>)null));
22+
private static readonly MethodInfo WhereMethod = ReflectionHelper.GetMethod(
23+
() => Enumerable.Where<Tuple>(null, (Func<Tuple, bool>)null));
2024

2125
public static void ReWrite(QueryModel queryModel, ISessionFactory sessionFactory)
2226
{
@@ -51,15 +55,11 @@ public static void ReWrite(QueryModel queryModel, ISessionFactory sessionFactory
5155
var keySelector = CreateSelector(elementExpression, 0);
5256

5357
var elementSelector = CreateSelector(elementExpression, 1);
54-
55-
var groupBy = EnumerableHelper.GetMethod("GroupBy",
56-
new[] { typeof (IEnumerable<>), typeof (Func<,>), typeof (Func<,>) },
57-
new[] { typeof (object[]), typeof (Tuple), typeof (Tuple) });
58-
58+
5959
var input = Expression.Parameter(typeof (IEnumerable<object>), "input");
6060

6161
var lambda = Expression.Lambda(
62-
Expression.Call(groupBy,
62+
Expression.Call(GroupByMethod,
6363
Expression.Call(CastMethod, input),
6464
keySelector,
6565
elementSelector),
@@ -154,24 +154,16 @@ private static LambdaExpression MakeSelector(ICollection<ExpressionHolder> eleme
154154
private static Expression SubCollectionQuery(System.Type collectionType, System.Type elementType, Expression source, Expression predicate, Expression selector)
155155
{
156156
// source.Where(predicate).Select(selector).ToList();
157-
var whereMethod = EnumerableHelper.GetMethod("Where",
158-
new[] { typeof (IEnumerable<>), typeof (Func<,>) },
159-
new[] { typeof (Tuple) });
160-
161157

162-
var selectMethod = EnumerableHelper.GetMethod("Select",
163-
new[] { typeof (IEnumerable<>), typeof (Func<,>) },
164-
new[] { typeof (Tuple), elementType });
158+
var selectMethod = ReflectionCache.EnumerableMethods.SelectDefinition.MakeGenericMethod(new[] { typeof(Tuple), elementType });
165159

166160
var select = Expression.Call(selectMethod,
167-
Expression.Call(whereMethod, source, predicate),
161+
Expression.Call(WhereMethod, source, predicate),
168162
selector);
169163

170164
if (collectionType.IsArray)
171165
{
172-
var toArrayMethod = EnumerableHelper.GetMethod("ToArray",
173-
new[] { typeof(IEnumerable<>) },
174-
new[] { elementType });
166+
var toArrayMethod = ReflectionCache.EnumerableMethods.ToArrayDefinition.MakeGenericMethod(new[] { elementType });
175167

176168
var array = Expression.Call(toArrayMethod, @select);
177169
return array;
@@ -181,9 +173,7 @@ private static Expression SubCollectionQuery(System.Type collectionType, System.
181173
if (constructor != null)
182174
return Expression.New(constructor, (Expression) @select);
183175

184-
var toListMethod = EnumerableHelper.GetMethod("ToList",
185-
new[] { typeof (IEnumerable<>) },
186-
new[] { elementType });
176+
var toListMethod = ReflectionCache.EnumerableMethods.ToListDefinition.MakeGenericMethod(new[] { elementType });
187177

188178
return Expression.Call(Expression.Call(toListMethod, @select),
189179
"AsReadonly",
Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
using System.Collections;
21
using System.Collections.Generic;
3-
using System.Linq;
42
using System.Linq.Expressions;
3+
using NHibernate.Util;
54
using Remotion.Linq.Clauses.ResultOperators;
65
using Remotion.Linq.Clauses.StreamedData;
76
using Remotion.Linq.Parsing.ExpressionTreeVisitors;
87

98
namespace NHibernate.Linq.Visitors.ResultOperatorProcessors
109
{
11-
public class ProcessAggregate : IResultOperatorProcessor<AggregateResultOperator>
12-
{
13-
public void Process(AggregateResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree)
14-
{
10+
public class ProcessAggregate : IResultOperatorProcessor<AggregateResultOperator>
11+
{
12+
public void Process(AggregateResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree)
13+
{
1514
var inputExpr = ((StreamedSequenceInfo)queryModelVisitor.PreviousEvaluationType).ItemExpression;
1615
var inputType = inputExpr.Type;
1716
var paramExpr = Expression.Parameter(inputType, "item");
@@ -22,11 +21,10 @@ public void Process(AggregateResultOperator resultOperator, QueryModelVisitor qu
2221

2322
var inputList = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(typeof(object)), "inputList");
2423

25-
var castToItem = EnumerableHelper.GetMethod("Cast", new[] { typeof(IEnumerable) }, new[] { inputType });
24+
var castToItem = ReflectionCache.EnumerableMethods.CastDefinition.MakeGenericMethod(new[] { inputType });
2625
var castToItemExpr = Expression.Call(castToItem, inputList);
2726

28-
var aggregate = ReflectionHelper.GetMethodDefinition(() => Enumerable.Aggregate<object>(null, null));
29-
aggregate = aggregate.GetGenericMethodDefinition().MakeGenericMethod(inputType);
27+
var aggregate = ReflectionCache.EnumerableMethods.AggregateDefinition.MakeGenericMethod(inputType);
3028

3129
MethodCallExpression call = Expression.Call(
3230
aggregate,
@@ -36,5 +34,5 @@ public void Process(AggregateResultOperator resultOperator, QueryModelVisitor qu
3634

3735
tree.AddListTransformer(Expression.Lambda(call, inputList));
3836
}
39-
}
37+
}
4038
}

0 commit comments

Comments
 (0)