1
+ using System ;
1
2
using System . Collections . Generic ;
2
3
using System . Linq ;
3
4
using System . Linq . Expressions ;
4
5
using NHibernate . Hql . Ast ;
6
+ using NHibernate . Linq . Expressions ;
5
7
using Remotion . Linq . Parsing ;
6
8
7
9
namespace NHibernate . Linq . Visitors
8
10
{
9
11
public class SelectClauseVisitor : ExpressionTreeVisitor
10
12
{
13
+ private readonly HqlTreeBuilder _hqlTreeBuilder = new HqlTreeBuilder ( ) ;
11
14
private HashSet < Expression > _hqlNodes ;
12
15
private readonly ParameterExpression _inputParameter ;
13
16
private readonly VisitorParameters _parameters ;
14
17
private int _iColumn ;
15
18
private List < HqlExpression > _hqlTreeNodes = new List < HqlExpression > ( ) ;
19
+ private readonly HqlGeneratorExpressionTreeVisitor _hqlVisitor ;
16
20
17
21
public SelectClauseVisitor ( System . Type inputType , VisitorParameters parameters )
18
22
{
19
23
_inputParameter = Expression . Parameter ( inputType , "input" ) ;
20
24
_parameters = parameters ;
25
+ _hqlVisitor = new HqlGeneratorExpressionTreeVisitor ( _parameters ) ;
21
26
}
22
27
23
28
public LambdaExpression ProjectionExpression { get ; private set ; }
@@ -29,19 +34,41 @@ public IEnumerable<HqlExpression> GetHqlNodes()
29
34
30
35
public void Visit ( Expression expression )
31
36
{
32
- // First, find the sub trees that can be expressed purely in HQL
33
- _hqlNodes = new SelectClauseHqlNominator ( _parameters ) . Nominate ( expression ) ;
37
+ var distinct = expression as NhDistinctExpression ;
38
+ if ( distinct != null )
39
+ {
40
+ expression = distinct . Expression ;
41
+ }
42
+
43
+ // Find the sub trees that can be expressed purely in HQL
44
+ var nominator = new SelectClauseHqlNominator ( _parameters ) ;
45
+ nominator . Visit ( expression ) ;
46
+ _hqlNodes = nominator . HqlCandidates ;
47
+
48
+ // Linq2SQL ignores calls to local methods. Linq2EF seems to not support
49
+ // calls to local methods at all. For NHibernate we support local methods,
50
+ // but prevent their use together with server-side distinct, since it may
51
+ // end up being wrong.
52
+ if ( distinct != null && nominator . ContainsUntranslatedMethodCalls )
53
+ throw new NotSupportedException ( "Cannot use distinct on result that depends on methods for which no SQL equivalent exist." ) ;
34
54
35
55
// Now visit the tree
36
- Expression projection = VisitExpression ( expression ) ;
56
+ var projection = VisitExpression ( expression ) ;
37
57
38
58
if ( ( projection != expression ) && ! _hqlNodes . Contains ( expression ) )
39
59
{
40
60
ProjectionExpression = Expression . Lambda ( projection , _inputParameter ) ;
41
61
}
42
62
43
- // Finally, handle any boolean results in the output nodes
63
+ // Handle any boolean results in the output nodes
44
64
_hqlTreeNodes = BooleanToCaseConvertor . Convert ( _hqlTreeNodes ) . ToList ( ) ;
65
+
66
+ if ( distinct != null )
67
+ {
68
+ var treeNodes = new List < HqlTreeNode > ( _hqlTreeNodes . Count + 1 ) { _hqlTreeBuilder . Distinct ( ) } ;
69
+ treeNodes . AddRange ( _hqlTreeNodes ) ;
70
+ _hqlTreeNodes = new List < HqlExpression > ( 1 ) { _hqlTreeBuilder . ExpressionSubTreeHolder ( treeNodes ) } ;
71
+ }
45
72
}
46
73
47
74
public override Expression VisitExpression ( Expression expression )
@@ -53,13 +80,10 @@ public override Expression VisitExpression(Expression expression)
53
80
54
81
if ( _hqlNodes . Contains ( expression ) )
55
82
{
56
- // Pure HQL evaluation - TODO - cache the Visitor?
57
- var hqlVisitor = new HqlGeneratorExpressionTreeVisitor ( _parameters ) ;
58
-
59
- _hqlTreeNodes . Add ( hqlVisitor . Visit ( expression ) . AsExpression ( ) ) ;
83
+ // Pure HQL evaluation
84
+ _hqlTreeNodes . Add ( _hqlVisitor . Visit ( expression ) . AsExpression ( ) ) ;
60
85
61
- return Expression . Convert (
62
- Expression . ArrayIndex ( _inputParameter , Expression . Constant ( _iColumn ++ ) ) , expression . Type ) ;
86
+ return Expression . Convert ( Expression . ArrayIndex ( _inputParameter , Expression . Constant ( _iColumn ++ ) ) , expression . Type ) ;
63
87
}
64
88
65
89
// Can't handle this node with HQL. Just recurse down, and emit the expression
0 commit comments