3
3
using System . Linq ;
4
4
using System . Linq . Expressions ;
5
5
using System . Reflection ;
6
+ using NHibernate . Engine ;
6
7
using NHibernate . Hql . Ast ;
7
8
using NHibernate . Linq . Clauses ;
8
9
using NHibernate . Linq . Expressions ;
12
13
using NHibernate . Linq . ResultOperators ;
13
14
using NHibernate . Linq . ReWriters ;
14
15
using NHibernate . Linq . Visitors . ResultOperatorProcessors ;
16
+ using NHibernate . Persister . Entity ;
15
17
using NHibernate . Util ;
16
18
using Remotion . Linq ;
17
19
using Remotion . Linq . Clauses ;
@@ -527,10 +529,13 @@ private void AddJoin(JoinClause joinClause, QueryModel queryModel, bool innerJoi
527
529
var withClause = equalityVisitor . Visit ( joinClause . InnerKeySelector , joinClause . OuterKeySelector ) ;
528
530
var alias = _hqlTree . TreeBuilder . Alias ( VisitorParameters . QuerySourceNamer . GetName ( joinClause ) ) ;
529
531
var joinExpression = HqlGeneratorExpressionVisitor . Visit ( joinClause . InnerSequence , VisitorParameters ) ;
532
+ var baseMemberCheker = new BaseMemberChecker ( VisitorParameters . SessionFactory ) ;
533
+
530
534
HqlTreeNode join ;
531
- // When associations are located inside the inner key selector we have to use a cross join instead of an inner
532
- // join and add the condition in the where statement.
533
- if ( queryModel . BodyClauses . OfType < NhJoinClause > ( ) . Any ( o => o . ParentJoinClause == joinClause ) )
535
+ // When associations or members from another table are located inside the inner key selector we have to use a cross join
536
+ // instead of an inner join and add the condition in the where statement.
537
+ if ( queryModel . BodyClauses . OfType < NhJoinClause > ( ) . Any ( o => o . ParentJoinClause == joinClause ) ||
538
+ queryModel . BodyClauses . OfType < JoinClause > ( ) . Any ( baseMemberCheker . ContainsBaseMember ) )
534
539
{
535
540
if ( ! innerJoin )
536
541
{
@@ -551,6 +556,54 @@ private void AddJoin(JoinClause joinClause, QueryModel queryModel, bool innerJoi
551
556
_hqlTree . AddFromClause ( join ) ;
552
557
}
553
558
559
+ private class BaseMemberChecker : NhExpressionVisitor
560
+ {
561
+ private readonly ISessionFactoryImplementor _sessionFactory ;
562
+ private bool _result ;
563
+
564
+ public BaseMemberChecker ( ISessionFactoryImplementor sessionFactory )
565
+ {
566
+ _sessionFactory = sessionFactory ;
567
+ }
568
+
569
+ public bool ContainsBaseMember ( JoinClause joinClause )
570
+ {
571
+ // Visit the join inner key only for entities that have subclasses
572
+ if ( joinClause . InnerSequence is ConstantExpression constantNode &&
573
+ constantNode . Value is IEntityNameProvider entityNameProvider &&
574
+ ! _sessionFactory . GetEntityPersister ( entityNameProvider . EntityName ) . EntityMetamodel . HasSubclasses )
575
+ {
576
+ return false ;
577
+ }
578
+
579
+ _result = false ;
580
+ Visit ( joinClause . InnerKeySelector ) ;
581
+
582
+ return _result ;
583
+ }
584
+
585
+ protected override Expression VisitMember ( MemberExpression node )
586
+ {
587
+ if ( ExpressionsHelper . TryGetMappedType (
588
+ _sessionFactory ,
589
+ node ,
590
+ out _ ,
591
+ out var persister ,
592
+ out _ ,
593
+ out var propertyPath ) &&
594
+ persister is IOuterJoinLoadable joinLoadable &&
595
+ joinLoadable . EntityMetamodel . GetIdentifierPropertyType ( propertyPath ) == null &&
596
+ joinLoadable . GetPropertyTableName ( propertyPath ) != joinLoadable . TableName
597
+ )
598
+ {
599
+ _result = true ;
600
+ return node ;
601
+ }
602
+
603
+ return base . VisitMember ( node ) ;
604
+ }
605
+ }
606
+
554
607
public override void VisitGroupJoinClause ( GroupJoinClause groupJoinClause , QueryModel queryModel , int index )
555
608
{
556
609
throw new NotImplementedException ( ) ;
0 commit comments