Skip to content

Fix joining with an inherited property from another table for Linq provider #2581

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 14, 2020
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