Skip to content

Commit 74c01bc

Browse files
authored
Fix nullable entity projection in hql (#2809)
Fixes #2772
1 parent 4441623 commit 74c01bc

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

src/NHibernate.Test/Async/Hql/EntityJoinHqlTest.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99

1010

1111
using System;
12+
using System.Linq;
1213
using System.Text.RegularExpressions;
1314
using NHibernate.Cfg.MappingSchema;
1415
using NHibernate.Mapping.ByCode;
1516
using NHibernate.Test.Hql.EntityJoinHqlTestEntities;
1617
using NUnit.Framework;
18+
using NHibernate.Linq;
1719

1820
namespace NHibernate.Test.Hql
1921
{
@@ -287,6 +289,24 @@ public async Task NullableOneToOneFetchQueryIsNotAffected2Async()
287289
}
288290
}
289291

292+
[Test(Description = "GH-2772")]
293+
public async Task NullableEntityProjectionAsync()
294+
{
295+
using (var session = OpenSession())
296+
using (session.BeginTransaction())
297+
{
298+
var nullableOwner1 = new NullableOwner {Name = "1", ManyToOne = await (session.LoadAsync<OneToOneEntity>(Guid.NewGuid()))};
299+
var nullableOwner2 = new NullableOwner {Name = "2"};
300+
await (session.SaveAsync(nullableOwner1));
301+
await (session.SaveAsync(nullableOwner2));
302+
303+
var fullList = await (session.Query<NullableOwner>().Select(x => new {x.Name, ManyToOneId = (Guid?) x.ManyToOne.Id}).ToListAsync());
304+
var withValidManyToOneList = await (session.Query<NullableOwner>().Where(x => x.ManyToOne != null).Select(x => new {x.Name, ManyToOneId = (Guid?) x.ManyToOne.Id}).ToListAsync());
305+
Assert.That(fullList.Count, Is.EqualTo(2));
306+
Assert.That(withValidManyToOneList.Count, Is.EqualTo(0));
307+
}
308+
}
309+
290310
[Test]
291311
public async Task EntityJoinWithEntityComparisonInWithClausShouldNotAddJoinAsync()
292312
{

src/NHibernate.Test/Hql/EntityJoinHqlTest.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using System.Text.RegularExpressions;
34
using NHibernate.Cfg.MappingSchema;
45
using NHibernate.Mapping.ByCode;
@@ -276,6 +277,24 @@ public void NullableOneToOneFetchQueryIsNotAffected2()
276277
}
277278
}
278279

280+
[Test(Description = "GH-2772")]
281+
public void NullableEntityProjection()
282+
{
283+
using (var session = OpenSession())
284+
using (session.BeginTransaction())
285+
{
286+
var nullableOwner1 = new NullableOwner {Name = "1", ManyToOne = session.Load<OneToOneEntity>(Guid.NewGuid())};
287+
var nullableOwner2 = new NullableOwner {Name = "2"};
288+
session.Save(nullableOwner1);
289+
session.Save(nullableOwner2);
290+
291+
var fullList = session.Query<NullableOwner>().Select(x => new {x.Name, ManyToOneId = (Guid?) x.ManyToOne.Id}).ToList();
292+
var withValidManyToOneList = session.Query<NullableOwner>().Where(x => x.ManyToOne != null).Select(x => new {x.Name, ManyToOneId = (Guid?) x.ManyToOne.Id}).ToList();
293+
Assert.That(fullList.Count, Is.EqualTo(2));
294+
Assert.That(withValidManyToOneList.Count, Is.EqualTo(0));
295+
}
296+
}
297+
279298
[Test]
280299
public void EntityJoinWithEntityComparisonInWithClausShouldNotAddJoin()
281300
{

src/NHibernate/Hql/Ast/ANTLR/Tree/DotNode.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ private void DereferenceEntity(EntityType entityType, bool implicitJoin, string
389389
//For nullable entity comparisons we always need to add join (like not constrained one-to-one or not-found ignore associations)
390390
//NOTE: This fix is not fully correct. It doesn't work for comparisons with null (where e.OneToOneProp is null)
391391
// as by default implicit join is generated and to work propelry left join is required (see GH-2611)
392-
bool comparisonWithNullableEntity = false;
392+
bool comparisonWithNullableEntity = entityType.IsNullable && Walker.IsComparativeExpressionClause;
393393

394394
if ( IsDotNode( parent ) )
395395
{
@@ -398,7 +398,7 @@ private void DereferenceEntity(EntityType entityType, bool implicitJoin, string
398398
// entity's PK (because 'our' table would know the FK).
399399
parentAsDotNode = ( DotNode ) parent;
400400
property = parentAsDotNode._propertyName;
401-
joinIsNeeded = generateJoin && ((Walker.IsSelectStatement && entityType.IsNullable) || !IsReferenceToPrimaryKey( parentAsDotNode._propertyName, entityType ));
401+
joinIsNeeded = generateJoin && ((Walker.IsSelectStatement && comparisonWithNullableEntity) || !IsReferenceToPrimaryKey( parentAsDotNode._propertyName, entityType ));
402402
}
403403
else if ( ! Walker.IsSelectStatement )
404404
{
@@ -411,7 +411,6 @@ private void DereferenceEntity(EntityType entityType, bool implicitJoin, string
411411
}
412412
else
413413
{
414-
comparisonWithNullableEntity = (Walker.IsComparativeExpressionClause && entityType.IsNullable);
415414
joinIsNeeded = generateJoin || (Walker.IsInSelect && !Walker.IsInCase) || (Walker.IsInFrom && !Walker.IsComparativeExpressionClause)
416415
|| comparisonWithNullableEntity;
417416
}

0 commit comments

Comments
 (0)