Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/NHibernate.Test/Async/Linq/ByMethod/JoinTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,13 @@ public async Task CanJoinOnEntityWithSubclassesAsync()
from o2 in db.Animals.Where(x => x.BodyWeight > 50)
select new {o, o2}).Take(1).ToListAsync());
}

[Test(Description = "GH-2580")]
public async Task CanInnerJoinOnSubclassWithBaseTableReferenceInOnClauseAsync()
{
var result = await ((from o in db.Animals
join o2 in db.Mammals on o.BodyWeight equals o2.BodyWeight
select new { o, o2 }).Take(1).ToListAsync());
}
}
}
8 changes: 8 additions & 0 deletions src/NHibernate.Test/Linq/ByMethod/JoinTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,13 @@ public void CanJoinOnEntityWithSubclasses()
from o2 in db.Animals.Where(x => x.BodyWeight > 50)
select new {o, o2}).Take(1).ToList();
}

[Test(Description = "GH-2580")]
public void CanInnerJoinOnSubclassWithBaseTableReferenceInOnClause()
{
var result = (from o in db.Animals
join o2 in db.Mammals on o.BodyWeight equals o2.BodyWeight
select new { o, o2 }).Take(1).ToList();
}
}
}
51 changes: 48 additions & 3 deletions src/NHibernate/Linq/Visitors/QueryModelVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using NHibernate.Engine;
using NHibernate.Hql.Ast;
using NHibernate.Linq.Clauses;
using NHibernate.Linq.Expressions;
Expand All @@ -12,6 +13,7 @@
using NHibernate.Linq.ResultOperators;
using NHibernate.Linq.ReWriters;
using NHibernate.Linq.Visitors.ResultOperatorProcessors;
using NHibernate.Persister.Entity;
using NHibernate.Util;
using Remotion.Linq;
using Remotion.Linq.Clauses;
Expand Down Expand Up @@ -527,10 +529,13 @@ private void AddJoin(JoinClause joinClause, QueryModel queryModel, bool innerJoi
var withClause = equalityVisitor.Visit(joinClause.InnerKeySelector, joinClause.OuterKeySelector);
var alias = _hqlTree.TreeBuilder.Alias(VisitorParameters.QuerySourceNamer.GetName(joinClause));
var joinExpression = HqlGeneratorExpressionVisitor.Visit(joinClause.InnerSequence, VisitorParameters);
var baseMemberCheker = new BaseMemberChecker(VisitorParameters.SessionFactory);

HqlTreeNode join;
// When associations are located inside the inner key selector we have to use a cross join instead of an inner
// join and add the condition in the where statement.
if (queryModel.BodyClauses.OfType<NhJoinClause>().Any(o => o.ParentJoinClause == joinClause))
// When associations or members from another table are located inside the inner key selector we have to use a cross join
// instead of an inner join and add the condition in the where statement.
if (queryModel.BodyClauses.OfType<NhJoinClause>().Any(o => o.ParentJoinClause == joinClause) ||
queryModel.BodyClauses.OfType<JoinClause>().Any(o => baseMemberCheker.ContainsBaseMember(o.InnerKeySelector)))
{
if (!innerJoin)
{
Expand All @@ -551,6 +556,46 @@ private void AddJoin(JoinClause joinClause, QueryModel queryModel, bool innerJoi
_hqlTree.AddFromClause(join);
}

private class BaseMemberChecker : NhExpressionVisitor
{
private readonly ISessionFactoryImplementor _sessionFactory;
private bool _result;

public BaseMemberChecker(ISessionFactoryImplementor sessionFactory)
{
_sessionFactory = sessionFactory;
}

public bool ContainsBaseMember(Expression node)
{
_result = false;
Visit(node);

return _result;
}

protected override Expression VisitMember(MemberExpression node)
{
if (ExpressionsHelper.TryGetMappedType(
_sessionFactory,
node,
out _,
out var persister,
out _,
out var propertyPath) &&
persister is IOuterJoinLoadable joinLoadable &&
joinLoadable.EntityMetamodel.GetIdentifierPropertyType(propertyPath) == null &&
joinLoadable.GetPropertyTableName(propertyPath) != joinLoadable.TableName
)
{
_result = true;
return node;
}

return base.VisitMember(node);
}
}

public override void VisitGroupJoinClause(GroupJoinClause groupJoinClause, QueryModel queryModel, int index)
{
throw new NotImplementedException();
Expand Down