Skip to content

Commit e0ca374

Browse files
committed
Avoid lambda compilation for member access expressions in LINQ
continuation of nhibernate#2948
1 parent bb1bf45 commit e0ca374

File tree

2 files changed

+88
-6
lines changed

2 files changed

+88
-6
lines changed

src/NHibernate/Engine/Query/QueryPlanCache.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
34
using System.Linq;
45
using System.Runtime.Serialization;
56
using NHibernate.Engine.Query.Sql;
@@ -66,12 +67,14 @@ public IQueryExpressionPlan GetHQLQueryPlan(IQueryExpression queryExpression, bo
6667
{
6768
log.Debug("unable to locate HQL query plan in cache; generating ({0})", queryExpression.Key);
6869
}
70+
Stopwatch stopwatch = log.IsWarnEnabled() ? Stopwatch.StartNew() : null;
6971
plan = new QueryExpressionPlan(queryExpression, shallow, enabledFilters, factory);
72+
stopwatch?.Stop();
7073
// 6.0 TODO: add "CanCachePlan { get; }" to IQueryExpression interface
7174
if (!(queryExpression is ICacheableQueryExpression linqExpression) || linqExpression.CanCachePlan)
7275
planCache.Put(key, plan);
7376
else
74-
log.Debug("Query plan not cacheable");
77+
log.Warn("Query plan is not cacheable QueryKey:{0}, ElapsedMilliseconds:{1}", queryExpression.Key, stopwatch.ElapsedMilliseconds);
7578
}
7679
else
7780
{

src/NHibernate/Impl/ExpressionProcessor.cs

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ private static ICriterion Le(ProjectionInfo property, object value)
248248
}
249249

250250
/// <summary>
251-
/// Invoke the expression to extract its runtime value
251+
/// Walk on expression extract its runtime value
252252
/// </summary>
253253
public static object FindValue(Expression expression)
254254
{
@@ -258,10 +258,10 @@ public static object FindValue(Expression expression)
258258
if (expression.NodeType == ExpressionType.MemberAccess)
259259
{
260260
var memberAccess = (MemberExpression) expression;
261+
var member = memberAccess.Member;
261262
if (memberAccess.Expression == null || memberAccess.Expression.NodeType == ExpressionType.Constant)
262263
{
263264
var constantValue = ((ConstantExpression) memberAccess.Expression)?.Value;
264-
var member = memberAccess.Member;
265265
switch (member.MemberType)
266266
{
267267
case MemberTypes.Field:
@@ -270,11 +270,90 @@ public static object FindValue(Expression expression)
270270
return ((PropertyInfo) member).GetValue(constantValue);
271271
}
272272
}
273+
else
274+
{
275+
switch (member.MemberType)
276+
{
277+
case MemberTypes.Field:
278+
return ((FieldInfo) member).GetValue(FindValue(memberAccess.Expression));
279+
case MemberTypes.Property:
280+
return ((PropertyInfo) member).GetValue(FindValue(memberAccess.Expression));
281+
}
282+
}
283+
}
284+
285+
if (expression.NodeType == ExpressionType.Call)
286+
{
287+
var methodCall = (MethodCallExpression) expression;
288+
289+
var args = new object[methodCall.Arguments.Count];
290+
for (int i = 0; i < args.Length; i++)
291+
args[i] = FindValue(methodCall.Arguments[i]);
292+
293+
if (methodCall.Object == null) //extension or static method
294+
{
295+
if (args.Length > 0 && args[0] != null)
296+
{
297+
return methodCall.Method.Invoke(args[0].GetType(), args);
298+
}
299+
else
300+
{
301+
return methodCall.Method.Invoke(methodCall.Method.DeclaringType, args);
302+
}
303+
}
304+
else
305+
{
306+
var callingObject = FindValue(methodCall.Object);
307+
308+
return methodCall.Method.Invoke(callingObject, args);
309+
}
310+
}
311+
312+
if (expression.NodeType == ExpressionType.Convert)
313+
{
314+
var convert = (UnaryExpression) expression;
315+
var value = FindValue(convert.Operand);
316+
if (value != null)
317+
{
318+
var type = Nullable.GetUnderlyingType(convert.Type) ?? convert.Type;
319+
if (type != convert.Operand.Type)
320+
{
321+
return convert.Method != null
322+
? convert.Method.Invoke(null, new[] { value })
323+
: Convert.ChangeType(value, type);
324+
}
325+
}
326+
327+
return value;
328+
}
329+
330+
if (expression is BinaryExpression binary)
331+
{
332+
dynamic left = FindValue(binary.Left);
333+
dynamic right = FindValue(binary.Right);
334+
if (binary.Method != null)
335+
{
336+
return binary.Method.Invoke(null, new[] { left, right });
337+
}
338+
else
339+
{
340+
switch (expression.NodeType)
341+
{
342+
case ExpressionType.Multiply:
343+
return left * right;
344+
case ExpressionType.Divide:
345+
return left / right;
346+
case ExpressionType.Modulo:
347+
return left % right;
348+
case ExpressionType.Add:
349+
return left + right;
350+
case ExpressionType.Subtract:
351+
return left - right;
352+
}
353+
}
273354
}
274355

275-
var valueExpression = Expression.Lambda(expression).Compile();
276-
object value = valueExpression.DynamicInvoke();
277-
return value;
356+
return Expression.Lambda(expression).Compile().DynamicInvoke();
278357
}
279358

280359
/// <summary>

0 commit comments

Comments
 (0)