33using System . Linq ;
44using System . Linq . Expressions ;
55using System . Reflection ;
6+ using NHibernate . Engine ;
67using NHibernate . Hql . Ast ;
78using NHibernate . Linq . Clauses ;
89using NHibernate . Linq . Expressions ;
1213using NHibernate . Linq . ResultOperators ;
1314using NHibernate . Linq . ReWriters ;
1415using NHibernate . Linq . Visitors . ResultOperatorProcessors ;
16+ using NHibernate . Persister . Entity ;
1517using NHibernate . Util ;
1618using Remotion . Linq ;
1719using Remotion . Linq . Clauses ;
@@ -527,10 +529,13 @@ private void AddJoin(JoinClause joinClause, QueryModel queryModel, bool innerJoi
527529 var withClause = equalityVisitor . Visit ( joinClause . InnerKeySelector , joinClause . OuterKeySelector ) ;
528530 var alias = _hqlTree . TreeBuilder . Alias ( VisitorParameters . QuerySourceNamer . GetName ( joinClause ) ) ;
529531 var joinExpression = HqlGeneratorExpressionVisitor . Visit ( joinClause . InnerSequence , VisitorParameters ) ;
532+ var baseMemberCheker = new BaseMemberChecker ( VisitorParameters . SessionFactory ) ;
533+
530534 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 ) )
534539 {
535540 if ( ! innerJoin )
536541 {
@@ -551,6 +556,54 @@ private void AddJoin(JoinClause joinClause, QueryModel queryModel, bool innerJoi
551556 _hqlTree . AddFromClause ( join ) ;
552557 }
553558
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+
554607 public override void VisitGroupJoinClause ( GroupJoinClause groupJoinClause , QueryModel queryModel , int index )
555608 {
556609 throw new NotImplementedException ( ) ;
0 commit comments