Skip to content

Commit 85ca554

Browse files
authored
Merge pull request #553 from alexDevBR/NH-3946
NH-3845, NH-3946 - Fix Linq queries with `.OfType<T>()` and `where is T`, where T is a mapped base type
2 parents 1920d17 + 06d17c7 commit 85ca554

File tree

7 files changed

+80
-18
lines changed

7 files changed

+80
-18
lines changed

src/NHibernate.Test/Linq/WhereTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,24 @@ where o is Dog
652652
Assert.That(query.Count, Is.EqualTo(2));
653653
}
654654

655+
[Test(Description = "NH-3946")]
656+
public void PolymorphicSearchOnObjectTypeWithIsKeyword()
657+
{
658+
var query = (from o in session.Query<Animal>()
659+
where o is Mammal
660+
select o).ToList();
661+
662+
Assert.That(query.Count, Is.EqualTo(3));
663+
}
664+
665+
[Test(Description = "NH-3845")]
666+
public void PolymorphicSearchOnObjectTypeWithOfType()
667+
{
668+
var query = session.Query<Animal>().OfType<Mammal>().ToList();
669+
670+
Assert.That(query.Count, Is.EqualTo(3));
671+
}
672+
655673
[Test]
656674
public void BitwiseQuery()
657675
{

src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,54 @@ protected HqlTreeNode VisitExpression(Expression expression)
147147

148148
private HqlTreeNode VisitTypeBinaryExpression(TypeBinaryExpression expression)
149149
{
150+
return BuildOfType(expression.Expression, expression.TypeOperand);
151+
}
152+
153+
internal HqlBooleanExpression BuildOfType(Expression expression, System.Type type)
154+
{
155+
var sessionFactory = _parameters.SessionFactory;
156+
var meta = sessionFactory.GetClassMetadata(type) as Persister.Entity.AbstractEntityPersister;
157+
if (meta != null && !meta.IsExplicitPolymorphism)
158+
{
159+
//Adapted the logic found in SingleTableEntityPersister.DiscriminatorFilterFragment
160+
var nodes = meta
161+
.SubclassClosure
162+
.Select(typeName => (NHibernate.Persister.Entity.IQueryable) sessionFactory.GetEntityPersister(typeName))
163+
.Where(persister => !persister.IsAbstract)
164+
.Select(persister => _hqlTreeBuilder.Ident(persister.EntityName))
165+
.ToList();
166+
167+
if (nodes.Count == 1)
168+
{
169+
return _hqlTreeBuilder.Equality(
170+
_hqlTreeBuilder.Dot(Visit(expression).AsExpression(), _hqlTreeBuilder.Class()),
171+
nodes[0]);
172+
}
173+
174+
if (nodes.Count > 1)
175+
{
176+
return _hqlTreeBuilder.In(
177+
_hqlTreeBuilder.Dot(
178+
Visit(expression).AsExpression(),
179+
_hqlTreeBuilder.Class()),
180+
_hqlTreeBuilder.ExpressionSubTreeHolder(nodes));
181+
}
182+
183+
if (nodes.Count == 0)
184+
{
185+
const string abstractClassWithNoSubclassExceptionMessageTemplate =
186+
@"The class {0} can't be instatiated and does not have mapped subclasses;
187+
possible solutions:
188+
- don't map the abstract class
189+
- map its subclasses.";
190+
191+
throw new NotSupportedException(string.Format(abstractClassWithNoSubclassExceptionMessageTemplate, meta.EntityName));
192+
}
193+
}
194+
150195
return _hqlTreeBuilder.Equality(
151-
_hqlTreeBuilder.Dot(Visit(expression.Expression).AsExpression(), _hqlTreeBuilder.Class()),
152-
_hqlTreeBuilder.Ident(expression.TypeOperand.FullName));
196+
_hqlTreeBuilder.Dot(Visit(expression).AsExpression(), _hqlTreeBuilder.Class()),
197+
_hqlTreeBuilder.Ident(type.FullName));
153198
}
154199

155200
protected HqlTreeNode VisitNhStar(NhStarExpression expression)
Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,17 @@
1-
using System.Linq.Expressions;
2-
using NHibernate.Hql.Ast;
3-
using Remotion.Linq.Clauses.ResultOperators;
1+
using Remotion.Linq.Clauses.ResultOperators;
42

53
namespace NHibernate.Linq.Visitors.ResultOperatorProcessors
64
{
75
public class ProcessOfType : IResultOperatorProcessor<OfTypeResultOperator>
86
{
9-
#region IResultOperatorProcessor<OfTypeResultOperator> Members
10-
117
public void Process(OfTypeResultOperator resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree)
128
{
13-
Expression source = queryModelVisitor.Model.SelectClause.GetOutputDataInfo().ItemExpression;
9+
var source = queryModelVisitor.Model.SelectClause.GetOutputDataInfo().ItemExpression;
1410

15-
tree.AddWhereClause(tree.TreeBuilder.Equality(
16-
tree.TreeBuilder.Dot(
17-
HqlGeneratorExpressionTreeVisitor.Visit(source, queryModelVisitor.VisitorParameters).AsExpression(),
18-
tree.TreeBuilder.Class()),
19-
tree.TreeBuilder.Ident(resultOperator.SearchedItemType.FullName)));
20-
}
11+
var expression = new HqlGeneratorExpressionTreeVisitor(queryModelVisitor.VisitorParameters)
12+
.BuildOfType(source, resultOperator.SearchedItemType);
2113

22-
#endregion
14+
tree.AddWhereClause(expression);
15+
}
2316
}
2417
}

src/NHibernate/Persister/Entity/AbstractEntityPersister.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,7 @@ public int[] NaturalIdentifierProperties
10131013
public abstract string[] ConstraintOrderedTableNameClosure { get;}
10141014
public abstract string DiscriminatorSQLValue { get;}
10151015
public abstract object DiscriminatorValue { get;}
1016+
public abstract string[] SubclassClosure { get; }
10161017
public abstract string[] PropertySpaces { get;}
10171018

10181019
protected virtual void AddDiscriminatorToInsert(SqlInsertBuilder insert) { }

src/NHibernate/Persister/Entity/JoinedSubclassEntityPersister.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,11 @@ public override object DiscriminatorValue
345345
get { return discriminatorValue; }
346346
}
347347

348+
public override string[] SubclassClosure
349+
{
350+
get { return subclassClosure; }
351+
}
352+
348353
public override string[] PropertySpaces
349354
{
350355
get

src/NHibernate/Persister/Entity/SingleTableEntityPersister.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ public override object DiscriminatorValue
424424
get { return discriminatorValue; }
425425
}
426426

427-
public virtual string[] SubclassClosure
427+
public override string[] SubclassClosure
428428
{
429429
get { return subclassClosure; }
430430
}
@@ -618,7 +618,7 @@ private string DiscriminatorFilterFragment(string alias)
618618
@"The class {0} can't be instatiated and does not have mapped subclasses;
619619
possible solutions:
620620
- don't map the abstract class
621-
- map the its subclasses.";
621+
- map its subclasses.";
622622

623623
if (NeedsDiscriminator)
624624
{

src/NHibernate/Persister/Entity/UnionSubclassEntityPersister.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ public override object DiscriminatorValue
186186
get { return discriminatorValue; }
187187
}
188188

189-
public string[] SubclassClosure
189+
public override string[] SubclassClosure
190190
{
191191
get { return subclassClosure; }
192192
}

0 commit comments

Comments
 (0)