diff --git a/src/NHibernate.Test/Linq/WhereTests.cs b/src/NHibernate.Test/Linq/WhereTests.cs index 2f8f66e5c0a..d35d9bc079e 100644 --- a/src/NHibernate.Test/Linq/WhereTests.cs +++ b/src/NHibernate.Test/Linq/WhereTests.cs @@ -652,6 +652,24 @@ where o is Dog Assert.That(query.Count, Is.EqualTo(2)); } + [Test(Description = "NH-3946")] + public void PolymorphicSearchOnObjectTypeWithIsKeyword() + { + var query = (from o in session.Query() + where o is Mammal + select o).ToList(); + + Assert.That(query.Count, Is.EqualTo(3)); + } + + [Test(Description = "NH-3845")] + public void PolymorphicSearchOnObjectTypeWithOfType() + { + var query = session.Query().OfType().ToList(); + + Assert.That(query.Count, Is.EqualTo(3)); + } + [Test] public void BitwiseQuery() { diff --git a/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs b/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs index 9b80a446a4b..4bdbffa05a7 100644 --- a/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs +++ b/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs @@ -147,9 +147,54 @@ protected HqlTreeNode VisitExpression(Expression expression) private HqlTreeNode VisitTypeBinaryExpression(TypeBinaryExpression expression) { + return BuildOfType(expression.Expression, expression.TypeOperand); + } + + internal HqlBooleanExpression BuildOfType(Expression expression, System.Type type) + { + var sessionFactory = _parameters.SessionFactory; + var meta = sessionFactory.GetClassMetadata(type) as Persister.Entity.AbstractEntityPersister; + if (meta != null && !meta.IsExplicitPolymorphism) + { + //Adapted the logic found in SingleTableEntityPersister.DiscriminatorFilterFragment + var nodes = meta + .SubclassClosure + .Select(typeName => (NHibernate.Persister.Entity.IQueryable) sessionFactory.GetEntityPersister(typeName)) + .Where(persister => !persister.IsAbstract) + .Select(persister => _hqlTreeBuilder.Ident(persister.EntityName)) + .ToList(); + + if (nodes.Count == 1) + { + return _hqlTreeBuilder.Equality( + _hqlTreeBuilder.Dot(Visit(expression).AsExpression(), _hqlTreeBuilder.Class()), + nodes[0]); + } + + if (nodes.Count > 1) + { + return _hqlTreeBuilder.In( + _hqlTreeBuilder.Dot( + Visit(expression).AsExpression(), + _hqlTreeBuilder.Class()), + _hqlTreeBuilder.ExpressionSubTreeHolder(nodes)); + } + + if (nodes.Count == 0) + { + const string abstractClassWithNoSubclassExceptionMessageTemplate = +@"The class {0} can't be instatiated and does not have mapped subclasses; +possible solutions: +- don't map the abstract class +- map its subclasses."; + + throw new NotSupportedException(string.Format(abstractClassWithNoSubclassExceptionMessageTemplate, meta.EntityName)); + } + } + return _hqlTreeBuilder.Equality( - _hqlTreeBuilder.Dot(Visit(expression.Expression).AsExpression(), _hqlTreeBuilder.Class()), - _hqlTreeBuilder.Ident(expression.TypeOperand.FullName)); + _hqlTreeBuilder.Dot(Visit(expression).AsExpression(), _hqlTreeBuilder.Class()), + _hqlTreeBuilder.Ident(type.FullName)); } protected HqlTreeNode VisitNhStar(NhStarExpression expression) diff --git a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessOfType.cs b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessOfType.cs index 7c9613df7a8..4da0271fee2 100644 --- a/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessOfType.cs +++ b/src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessOfType.cs @@ -1,24 +1,17 @@ -using System.Linq.Expressions; -using NHibernate.Hql.Ast; -using Remotion.Linq.Clauses.ResultOperators; +using Remotion.Linq.Clauses.ResultOperators; namespace NHibernate.Linq.Visitors.ResultOperatorProcessors { public class ProcessOfType : IResultOperatorProcessor { - #region IResultOperatorProcessor Members - public void Process(OfTypeResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree) { - Expression source = queryModelVisitor.Model.SelectClause.GetOutputDataInfo().ItemExpression; + var source = queryModelVisitor.Model.SelectClause.GetOutputDataInfo().ItemExpression; - tree.AddWhereClause(tree.TreeBuilder.Equality( - tree.TreeBuilder.Dot( - HqlGeneratorExpressionTreeVisitor.Visit(source, queryModelVisitor.VisitorParameters).AsExpression(), - tree.TreeBuilder.Class()), - tree.TreeBuilder.Ident(resultOperator.SearchedItemType.FullName))); - } + var expression = new HqlGeneratorExpressionTreeVisitor(queryModelVisitor.VisitorParameters) + .BuildOfType(source, resultOperator.SearchedItemType); - #endregion + tree.AddWhereClause(expression); + } } } \ No newline at end of file diff --git a/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs b/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs index f817f5647d9..c0b98bb0eeb 100644 --- a/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs +++ b/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs @@ -1013,6 +1013,7 @@ public int[] NaturalIdentifierProperties public abstract string[] ConstraintOrderedTableNameClosure { get;} public abstract string DiscriminatorSQLValue { get;} public abstract object DiscriminatorValue { get;} + public abstract string[] SubclassClosure { get; } public abstract string[] PropertySpaces { get;} protected virtual void AddDiscriminatorToInsert(SqlInsertBuilder insert) { } diff --git a/src/NHibernate/Persister/Entity/JoinedSubclassEntityPersister.cs b/src/NHibernate/Persister/Entity/JoinedSubclassEntityPersister.cs index 63aae0dcc44..c1510d9409c 100644 --- a/src/NHibernate/Persister/Entity/JoinedSubclassEntityPersister.cs +++ b/src/NHibernate/Persister/Entity/JoinedSubclassEntityPersister.cs @@ -345,6 +345,11 @@ public override object DiscriminatorValue get { return discriminatorValue; } } + public override string[] SubclassClosure + { + get { return subclassClosure; } + } + public override string[] PropertySpaces { get diff --git a/src/NHibernate/Persister/Entity/SingleTableEntityPersister.cs b/src/NHibernate/Persister/Entity/SingleTableEntityPersister.cs index 0131d84aafa..7ce36aaa03f 100644 --- a/src/NHibernate/Persister/Entity/SingleTableEntityPersister.cs +++ b/src/NHibernate/Persister/Entity/SingleTableEntityPersister.cs @@ -424,7 +424,7 @@ public override object DiscriminatorValue get { return discriminatorValue; } } - public virtual string[] SubclassClosure + public override string[] SubclassClosure { get { return subclassClosure; } } @@ -618,7 +618,7 @@ private string DiscriminatorFilterFragment(string alias) @"The class {0} can't be instatiated and does not have mapped subclasses; possible solutions: - don't map the abstract class -- map the its subclasses."; +- map its subclasses."; if (NeedsDiscriminator) { diff --git a/src/NHibernate/Persister/Entity/UnionSubclassEntityPersister.cs b/src/NHibernate/Persister/Entity/UnionSubclassEntityPersister.cs index a1a3d5267bf..ad93180dc2d 100644 --- a/src/NHibernate/Persister/Entity/UnionSubclassEntityPersister.cs +++ b/src/NHibernate/Persister/Entity/UnionSubclassEntityPersister.cs @@ -186,7 +186,7 @@ public override object DiscriminatorValue get { return discriminatorValue; } } - public string[] SubclassClosure + public override string[] SubclassClosure { get { return subclassClosure; } }