Skip to content

Commit c53c2e2

Browse files
authored
Fix join on interface in Linq/hql (#2810)
Fixes #2805
1 parent 15e4a73 commit c53c2e2

File tree

5 files changed

+54
-13
lines changed

5 files changed

+54
-13
lines changed

src/NHibernate.Test/Async/Linq/ByMethod/JoinTests.cs

+9
Original file line numberDiff line numberDiff line change
@@ -156,5 +156,14 @@ public async Task CanInnerJoinOnSubclassWithBaseTableReferenceInOnClauseAsync()
156156
join o2 in db.Mammals on o.BodyWeight equals o2.BodyWeight
157157
select new { o, o2 }).Take(1).ToListAsync());
158158
}
159+
160+
[Test(Description = "GH-2805")]
161+
public async Task CanJoinOnInterfaceAsync()
162+
{
163+
var result = await (db.IUsers.Join(db.IUsers,
164+
u => u.Id,
165+
iu => iu.Id,
166+
(u, iu) => iu.Name).Take(1).ToListAsync());
167+
}
159168
}
160169
}

src/NHibernate.Test/Linq/ByMethod/JoinTests.cs

+9
Original file line numberDiff line numberDiff line change
@@ -145,5 +145,14 @@ public void CanInnerJoinOnSubclassWithBaseTableReferenceInOnClause()
145145
join o2 in db.Mammals on o.BodyWeight equals o2.BodyWeight
146146
select new { o, o2 }).Take(1).ToList();
147147
}
148+
149+
[Test(Description = "GH-2805")]
150+
public void CanJoinOnInterface()
151+
{
152+
var result = db.IUsers.Join(db.IUsers,
153+
u => u.Id,
154+
iu => iu.Id,
155+
(u, iu) => iu.Name).Take(1).ToList();
156+
}
148157
}
149158
}

src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs

+27-5
Original file line numberDiff line numberDiff line change
@@ -792,17 +792,39 @@ private EntityJoinFromElement CreateEntityJoin(
792792

793793
private IQueryable ResolveEntityJoinReferencedPersister(IASTNode path)
794794
{
795+
string entityName = GetEntityJoinCandidateEntityName(path);
796+
797+
var persister = SessionFactoryHelper.FindQueryableUsingImports(entityName);
798+
if (persister == null && entityName != null)
799+
{
800+
var implementors = SessionFactoryHelper.Factory.GetImplementors(entityName);
801+
//Possible case - join on interface
802+
if (implementors.Length == 1)
803+
persister = SessionFactoryHelper.FindQueryableUsingImports(implementors[0]);
804+
}
805+
806+
if (persister != null)
807+
return persister;
808+
795809
if (path.Type == IDENT)
796810
{
797-
var pathIdentNode = (IdentNode) path;
798811
// Since IDENT node is not expected for implicit join path, we can throw on not found persister
799-
return (IQueryable) SessionFactoryHelper.RequireClassPersister(pathIdentNode.Path);
812+
throw new QuerySyntaxException(entityName + " is not mapped");
800813
}
801-
else if (path.Type == DOT)
814+
815+
return null;
816+
}
817+
818+
private static string GetEntityJoinCandidateEntityName(IASTNode path)
819+
{
820+
switch (path.Type)
802821
{
803-
var pathText = ASTUtil.GetPathText(path);
804-
return SessionFactoryHelper.FindQueryableUsingImports(pathText);
822+
case IDENT:
823+
return ((IdentNode) path).Path;
824+
case DOT:
825+
return ASTUtil.GetPathText(path);
805826
}
827+
806828
return null;
807829
}
808830

src/NHibernate/Impl/SessionFactoryImpl.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ public string[] GetImplementors(string entityOrClassName)
745745
{
746746
// try to get the class from imported names
747747
string importedName = GetImportedClassName(entityOrClassName);
748-
if (importedName != null)
748+
if (importedName != entityOrClassName)
749749
{
750750
clazz = System.Type.GetType(importedName, false);
751751
}

src/NHibernate/Linq/Visitors/QueryModelVisitor.cs

+8-7
Original file line numberDiff line numberDiff line change
@@ -570,16 +570,17 @@ public bool ContainsBaseMember(JoinClause joinClause)
570570
{
571571
// Visit the join inner key only for entities that have subclasses
572572
if (joinClause.InnerSequence is ConstantExpression constantNode &&
573-
constantNode.Value is IEntityNameProvider entityNameProvider &&
574-
!_sessionFactory.GetEntityPersister(entityNameProvider.EntityName).EntityMetamodel.HasSubclasses)
573+
constantNode.Value is IEntityNameProvider &&
574+
ExpressionsHelper.TryGetMappedType(_sessionFactory, constantNode, out _, out var _persister, out _, out _) &&
575+
_persister?.EntityMetamodel.HasSubclasses == true)
575576
{
576-
return false;
577-
}
577+
_result = false;
578+
Visit(joinClause.InnerKeySelector);
578579

579-
_result = false;
580-
Visit(joinClause.InnerKeySelector);
580+
return _result;
581+
}
581582

582-
return _result;
583+
return false;
583584
}
584585

585586
protected override Expression VisitMember(MemberExpression node)

0 commit comments

Comments
 (0)