1
1
using System ;
2
2
using System . Linq ;
3
3
using System . Linq . Expressions ;
4
+ using NHibernate . Collection ;
4
5
using Remotion . Linq . Clauses . Expressions ;
5
6
using Remotion . Linq . Parsing ;
6
- using Remotion . Linq . Parsing . ExpressionVisitors ;
7
7
using Remotion . Linq . Parsing . ExpressionVisitors . TreeEvaluation ;
8
8
9
9
namespace NHibernate . Linq . Visitors
10
10
{
11
+ //Modified version of PartialEvaluatingExpressionVisitor from Relinq (https://github.com/re-motion/Relinq)
12
+ //Copyright (c) rubicon IT GmbH, www.rubicon.eu
13
+ //
14
+ //Used under the Apache Software License 2.0
11
15
internal class NhPartialEvaluatingExpressionVisitor : RelinqExpressionVisitor , IPartialEvaluationExceptionExpressionVisitor
12
16
{
17
+ public static Expression EvaluateIndependentSubtrees ( Expression expression )
18
+ {
19
+ return EvaluateIndependentSubtrees ( expression , new NhEvaluatableExpressionFilter ( ) ) ;
20
+ }
21
+ /// <summary>
22
+ /// Takes an expression tree and finds and evaluates all its evaluatable subtrees.
23
+ /// </summary>
24
+ public static Expression EvaluateIndependentSubtrees ( Expression expressionTree , IEvaluatableExpressionFilter evaluatableExpressionFilter )
25
+ {
26
+ if ( expressionTree == null ) throw new ArgumentNullException ( nameof ( expressionTree ) ) ;
27
+ if ( evaluatableExpressionFilter == null ) throw new ArgumentNullException ( nameof ( evaluatableExpressionFilter ) ) ;
28
+
29
+ var partialEvaluationInfo = EvaluatableTreeFindingExpressionVisitor . Analyze ( expressionTree , evaluatableExpressionFilter ) ;
30
+
31
+ var visitor = new NhPartialEvaluatingExpressionVisitor ( partialEvaluationInfo , evaluatableExpressionFilter ) ;
32
+ return visitor . Visit ( expressionTree ) ;
33
+ }
34
+
35
+ // _partialEvaluationInfo contains a list of the expressions that are safe to be evaluated.
36
+ private readonly PartialEvaluationInfo _partialEvaluationInfo ;
37
+ private readonly IEvaluatableExpressionFilter _evaluatableExpressionFilter ;
38
+
39
+ private NhPartialEvaluatingExpressionVisitor (
40
+ PartialEvaluationInfo partialEvaluationInfo ,
41
+ IEvaluatableExpressionFilter evaluatableExpressionFilter )
42
+ {
43
+ _partialEvaluationInfo = partialEvaluationInfo ?? throw new ArgumentNullException ( nameof ( partialEvaluationInfo ) ) ;
44
+ _evaluatableExpressionFilter = evaluatableExpressionFilter ?? throw new ArgumentNullException ( nameof ( evaluatableExpressionFilter ) ) ;
45
+ }
46
+
47
+ public override Expression Visit ( Expression expression )
48
+ {
49
+ // Only evaluate expressions which do not use any of the surrounding parameter expressions. Don't evaluate
50
+ // lambda expressions (even if you could), we want to analyze those later on.
51
+ if ( expression == null )
52
+ return null ;
53
+
54
+ if ( expression . NodeType == ExpressionType . Lambda || ! _partialEvaluationInfo . IsEvaluatableExpression ( expression ) )
55
+ return base . Visit ( expression ) ;
56
+
57
+ Expression evaluatedExpression ;
58
+ try
59
+ {
60
+ evaluatedExpression = EvaluateSubtree ( expression ) ;
61
+ }
62
+ catch ( Exception ex )
63
+ {
64
+ // Evaluation caused an exception. Skip evaluation of this expression and proceed as if it weren't evaluable.
65
+ var baseVisitedExpression = base . Visit ( expression ) ;
66
+ // Then wrap the result to capture the exception for the back-end.
67
+ return new PartialEvaluationExceptionExpression ( ex , baseVisitedExpression ) ;
68
+ }
69
+
70
+ if ( evaluatedExpression != expression )
71
+ return EvaluateIndependentSubtrees ( evaluatedExpression , _evaluatableExpressionFilter ) ;
72
+
73
+ return evaluatedExpression ;
74
+ }
75
+
76
+ /// <summary>
77
+ /// Evaluates an evaluatable <see cref="Expression"/> subtree, i.e. an independent expression tree that is compilable and executable
78
+ /// without any data being passed in. The result of the evaluation is returned as a <see cref="ConstantExpression"/>; if the subtree
79
+ /// is already a <see cref="ConstantExpression"/>, no evaluation is performed.
80
+ /// </summary>
81
+ /// <param name="subtree">The subtree to be evaluated.</param>
82
+ /// <returns>A <see cref="ConstantExpression"/> holding the result of the evaluation.</returns>
83
+ private Expression EvaluateSubtree ( Expression subtree )
84
+ {
85
+ if ( subtree == null ) throw new ArgumentNullException ( nameof ( subtree ) ) ;
86
+
87
+ if ( subtree . NodeType == ExpressionType . Constant )
88
+ {
89
+ var constantExpression = ( ConstantExpression ) subtree ;
90
+ var valueAsIQueryable = constantExpression . Value as IQueryable ;
91
+ if ( valueAsIQueryable != null && ! IsEvaluatedQueryable ( constantExpression . Value ) && valueAsIQueryable . Expression != constantExpression )
92
+ return valueAsIQueryable . Expression ;
93
+
94
+ return constantExpression ;
95
+ }
96
+ Expression < Func < object > > lambdaWithoutParameters = Expression . Lambda < Func < object > > ( Expression . Convert ( subtree , typeof ( object ) ) ) ;
97
+ var compiledLambda = lambdaWithoutParameters . Compile ( ) ;
98
+
99
+ object value = compiledLambda ( ) ;
100
+ return Expression . Constant ( value , subtree . Type ) ;
101
+ }
102
+
103
+ protected virtual bool IsEvaluatedQueryable ( object queryable )
104
+ {
105
+ return queryable is IPersistentCollection ;
106
+ }
107
+
13
108
protected override Expression VisitConstant ( ConstantExpression expression )
14
109
{
15
110
var value = expression . Value as Expression ;
@@ -21,12 +116,6 @@ protected override Expression VisitConstant(ConstantExpression expression)
21
116
return EvaluateIndependentSubtrees ( value ) ;
22
117
}
23
118
24
- public static Expression EvaluateIndependentSubtrees ( Expression expression )
25
- {
26
- var evaluatedExpression = PartialEvaluatingExpressionVisitor . EvaluateIndependentSubtrees ( expression , new NhEvaluatableExpressionFilter ( ) ) ;
27
- return new NhPartialEvaluatingExpressionVisitor ( ) . Visit ( evaluatedExpression ) ;
28
- }
29
-
30
119
public Expression VisitPartialEvaluationException ( PartialEvaluationExceptionExpression partialEvaluationExceptionExpression )
31
120
{
32
121
throw new HibernateException (
@@ -35,6 +124,8 @@ public Expression VisitPartialEvaluationException(PartialEvaluationExceptionExpr
35
124
}
36
125
}
37
126
127
+
128
+
38
129
internal class NhEvaluatableExpressionFilter : EvaluatableExpressionFilterBase
39
130
{
40
131
public override bool IsEvaluatableMethodCall ( MethodCallExpression node )
0 commit comments