@@ -20,11 +20,13 @@ public class AddJoinsReWriter : NhQueryModelVisitorBase, IIsEntityDecider
20
20
private readonly ISessionFactoryImplementor _sessionFactory ;
21
21
private readonly MemberExpressionJoinDetector _memberExpressionJoinDetector ;
22
22
private readonly WhereJoinDetector _whereJoinDetector ;
23
+ private int ? _joinInsertIndex ;
24
+ private JoinClause _currentJoin ;
23
25
24
26
private AddJoinsReWriter ( ISessionFactoryImplementor sessionFactory , QueryModel queryModel )
25
27
{
26
28
_sessionFactory = sessionFactory ;
27
- var joiner = new Joiner ( queryModel ) ;
29
+ var joiner = new Joiner ( queryModel , AddJoin ) ;
28
30
_memberExpressionJoinDetector = new MemberExpressionJoinDetector ( this , joiner ) ;
29
31
_whereJoinDetector = new WhereJoinDetector ( this , joiner ) ;
30
32
}
@@ -62,20 +64,25 @@ public override void VisitNhHavingClause(NhHavingClause havingClause, QueryModel
62
64
63
65
public override void VisitJoinClause ( JoinClause joinClause , QueryModel queryModel , int index )
64
66
{
65
- // When there are association navigations inside an on clause (e.g. c.ContactTitle equals o.Customer.ContactTitle),
67
+ VisitJoinClause ( joinClause , queryModel , joinClause ) ;
68
+ }
69
+
70
+ private void VisitJoinClause ( JoinClause joinClause , QueryModel queryModel , IBodyClause bodyClause )
71
+ {
72
+ joinClause . InnerSequence = _whereJoinDetector . Transform ( joinClause . InnerSequence ) ;
73
+
74
+ // When associations are located in the outer key (e.g. from a in A join b in B b on a.C.D.Id equals b.Id),
75
+ // we have to insert the association join before the current join in order to produce a valid query.
76
+ _joinInsertIndex = queryModel . BodyClauses . IndexOf ( bodyClause ) ;
77
+ joinClause . OuterKeySelector = _whereJoinDetector . Transform ( joinClause . OuterKeySelector ) ;
78
+ _joinInsertIndex = null ;
79
+
80
+ // When associations are located in the inner key (e.g. from a in A join b in B b on a.Id equals b.C.D.Id),
66
81
// we have to move the condition to the where statement, otherwise the query will be invalid.
67
82
// Link newly created joins with the current join clause in order to later detect which join type to use.
68
- queryModel . BodyClauses . CollectionChanged += OnCollectionChange ;
69
- _whereJoinDetector . Transform ( joinClause ) ;
70
- queryModel . BodyClauses . CollectionChanged -= OnCollectionChange ;
71
-
72
- void OnCollectionChange ( object sender , NotifyCollectionChangedEventArgs e )
73
- {
74
- foreach ( var nhJoinClause in e . NewItems . OfType < NhJoinClause > ( ) )
75
- {
76
- nhJoinClause . RelatedBodyClause = joinClause ;
77
- }
78
- }
83
+ _currentJoin = joinClause ;
84
+ joinClause . InnerKeySelector = _whereJoinDetector . Transform ( joinClause . InnerKeySelector ) ;
85
+ _currentJoin = null ;
79
86
}
80
87
81
88
public bool IsEntity ( System . Type type )
@@ -88,5 +95,19 @@ public bool IsIdentifier(System.Type type, string propertyName)
88
95
var metadata = _sessionFactory . GetClassMetadata ( type ) ;
89
96
return metadata != null && propertyName . Equals ( metadata . IdentifierPropertyName ) ;
90
97
}
98
+
99
+ private void AddJoin ( QueryModel queryModel , NhJoinClause joinClause )
100
+ {
101
+ joinClause . ParentJoinClause = _currentJoin ;
102
+ if ( _joinInsertIndex . HasValue )
103
+ {
104
+ queryModel . BodyClauses . Insert ( _joinInsertIndex . Value , joinClause ) ;
105
+ _joinInsertIndex ++ ;
106
+ }
107
+ else
108
+ {
109
+ queryModel . BodyClauses . Add ( joinClause ) ;
110
+ }
111
+ }
91
112
}
92
113
}
0 commit comments