From c28d2fc23b416e00d6e5cd733e66ed8feeb49b34 Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Thu, 3 Jun 2021 12:09:43 +0300 Subject: [PATCH 1/2] Fix join on interface --- .../Async/Linq/ByMethod/JoinTests.cs | 9 ++++++ .../Linq/ByMethod/JoinTests.cs | 9 ++++++ src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs | 32 ++++++++++++++++--- .../Linq/Visitors/QueryModelVisitor.cs | 15 +++++---- 4 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/NHibernate.Test/Async/Linq/ByMethod/JoinTests.cs b/src/NHibernate.Test/Async/Linq/ByMethod/JoinTests.cs index 9fda2e873c9..e51ac68f1e6 100644 --- a/src/NHibernate.Test/Async/Linq/ByMethod/JoinTests.cs +++ b/src/NHibernate.Test/Async/Linq/ByMethod/JoinTests.cs @@ -156,5 +156,14 @@ public async Task CanInnerJoinOnSubclassWithBaseTableReferenceInOnClauseAsync() join o2 in db.Mammals on o.BodyWeight equals o2.BodyWeight select new { o, o2 }).Take(1).ToListAsync()); } + + [Test(Description = "GH-2805")] + public async Task CanJoinOnInterfaceAsync() + { + var result = await (db.IUsers.Join(db.IUsers, + u => u.Id, + iu => iu.Id, + (u, iu) => iu.Name).Take(1).ToListAsync()); + } } } diff --git a/src/NHibernate.Test/Linq/ByMethod/JoinTests.cs b/src/NHibernate.Test/Linq/ByMethod/JoinTests.cs index a7a4750b87a..bba8b52a638 100644 --- a/src/NHibernate.Test/Linq/ByMethod/JoinTests.cs +++ b/src/NHibernate.Test/Linq/ByMethod/JoinTests.cs @@ -145,5 +145,14 @@ public void CanInnerJoinOnSubclassWithBaseTableReferenceInOnClause() join o2 in db.Mammals on o.BodyWeight equals o2.BodyWeight select new { o, o2 }).Take(1).ToList(); } + + [Test(Description = "GH-2805")] + public void CanJoinOnInterface() + { + var result = db.IUsers.Join(db.IUsers, + u => u.Id, + iu => iu.Id, + (u, iu) => iu.Name).Take(1).ToList(); + } } } diff --git a/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs b/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs index 1d36eefe1e1..877286c2470 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs @@ -792,17 +792,39 @@ private EntityJoinFromElement CreateEntityJoin( private IQueryable ResolveEntityJoinReferencedPersister(IASTNode path) { + string entityName = GetEntityJoinCandidateEntityName(path); + + var persister = SessionFactoryHelper.FindQueryableUsingImports(entityName); + if (persister == null && entityName != null) + { + var implementors = SessionFactoryHelper.Factory.GetImplementors(entityName); + //Possible case - join on interface + if (implementors.Length == 1) + persister = SessionFactoryHelper.FindQueryableUsingImports(implementors[0]); + } + + if (persister != null) + return persister; + if (path.Type == IDENT) { - var pathIdentNode = (IdentNode) path; // Since IDENT node is not expected for implicit join path, we can throw on not found persister - return (IQueryable) SessionFactoryHelper.RequireClassPersister(pathIdentNode.Path); + throw new QuerySyntaxException(entityName + " is not mapped"); } - else if (path.Type == DOT) + + return null; + } + + private static string GetEntityJoinCandidateEntityName(IASTNode path) + { + switch (path.Type) { - var pathText = ASTUtil.GetPathText(path); - return SessionFactoryHelper.FindQueryableUsingImports(pathText); + case IDENT: + return ((IdentNode) path).Path; + case DOT: + return ASTUtil.GetPathText(path); } + return null; } diff --git a/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs b/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs index 62abaae77cb..be6851fb4eb 100644 --- a/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs +++ b/src/NHibernate/Linq/Visitors/QueryModelVisitor.cs @@ -570,16 +570,17 @@ public bool ContainsBaseMember(JoinClause joinClause) { // Visit the join inner key only for entities that have subclasses if (joinClause.InnerSequence is ConstantExpression constantNode && - constantNode.Value is IEntityNameProvider entityNameProvider && - !_sessionFactory.GetEntityPersister(entityNameProvider.EntityName).EntityMetamodel.HasSubclasses) + constantNode.Value is IEntityNameProvider && + ExpressionsHelper.TryGetMappedType(_sessionFactory, constantNode, out _, out var _persister, out _, out _) && + _persister?.EntityMetamodel.HasSubclasses == true) { - return false; - } + _result = false; + Visit(joinClause.InnerKeySelector); - _result = false; - Visit(joinClause.InnerKeySelector); + return _result; + } - return _result; + return false; } protected override Expression VisitMember(MemberExpression node) From b07d3c2ad3f390b960f2a4cd5b6e52d1698396fe Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Thu, 3 Jun 2021 14:43:22 +0300 Subject: [PATCH 2/2] Fix GetImplementors exception on GetType --- src/NHibernate/Impl/SessionFactoryImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NHibernate/Impl/SessionFactoryImpl.cs b/src/NHibernate/Impl/SessionFactoryImpl.cs index a4b72cea96d..6c68e639d12 100644 --- a/src/NHibernate/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Impl/SessionFactoryImpl.cs @@ -745,7 +745,7 @@ public string[] GetImplementors(string entityOrClassName) { // try to get the class from imported names string importedName = GetImportedClassName(entityOrClassName); - if (importedName != null) + if (importedName != entityOrClassName) { clazz = System.Type.GetType(importedName, false); }