diff --git a/src/NHibernate.Test/Async/Hql/EntityJoinHqlTest.cs b/src/NHibernate.Test/Async/Hql/EntityJoinHqlTest.cs index adfd4d7ed49..10def317de4 100644 --- a/src/NHibernate.Test/Async/Hql/EntityJoinHqlTest.cs +++ b/src/NHibernate.Test/Async/Hql/EntityJoinHqlTest.cs @@ -9,11 +9,13 @@ using System; +using System.Linq; using System.Text.RegularExpressions; using NHibernate.Cfg.MappingSchema; using NHibernate.Mapping.ByCode; using NHibernate.Test.Hql.EntityJoinHqlTestEntities; using NUnit.Framework; +using NHibernate.Linq; namespace NHibernate.Test.Hql { @@ -287,6 +289,24 @@ public async Task NullableOneToOneFetchQueryIsNotAffected2Async() } } + [Test(Description = "GH-2772")] + public async Task NullableEntityProjectionAsync() + { + using (var session = OpenSession()) + using (session.BeginTransaction()) + { + var nullableOwner1 = new NullableOwner {Name = "1", ManyToOne = await (session.LoadAsync(Guid.NewGuid()))}; + var nullableOwner2 = new NullableOwner {Name = "2"}; + await (session.SaveAsync(nullableOwner1)); + await (session.SaveAsync(nullableOwner2)); + + var fullList = await (session.Query().Select(x => new {x.Name, ManyToOneId = (Guid?) x.ManyToOne.Id}).ToListAsync()); + var withValidManyToOneList = await (session.Query().Where(x => x.ManyToOne != null).Select(x => new {x.Name, ManyToOneId = (Guid?) x.ManyToOne.Id}).ToListAsync()); + Assert.That(fullList.Count, Is.EqualTo(2)); + Assert.That(withValidManyToOneList.Count, Is.EqualTo(0)); + } + } + [Test] public async Task EntityJoinWithEntityComparisonInWithClausShouldNotAddJoinAsync() { diff --git a/src/NHibernate.Test/Hql/EntityJoinHqlTest.cs b/src/NHibernate.Test/Hql/EntityJoinHqlTest.cs index cc2cc1da4a9..ecc64c5a5c4 100644 --- a/src/NHibernate.Test/Hql/EntityJoinHqlTest.cs +++ b/src/NHibernate.Test/Hql/EntityJoinHqlTest.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Text.RegularExpressions; using NHibernate.Cfg.MappingSchema; using NHibernate.Mapping.ByCode; @@ -276,6 +277,24 @@ public void NullableOneToOneFetchQueryIsNotAffected2() } } + [Test(Description = "GH-2772")] + public void NullableEntityProjection() + { + using (var session = OpenSession()) + using (session.BeginTransaction()) + { + var nullableOwner1 = new NullableOwner {Name = "1", ManyToOne = session.Load(Guid.NewGuid())}; + var nullableOwner2 = new NullableOwner {Name = "2"}; + session.Save(nullableOwner1); + session.Save(nullableOwner2); + + var fullList = session.Query().Select(x => new {x.Name, ManyToOneId = (Guid?) x.ManyToOne.Id}).ToList(); + var withValidManyToOneList = session.Query().Where(x => x.ManyToOne != null).Select(x => new {x.Name, ManyToOneId = (Guid?) x.ManyToOne.Id}).ToList(); + Assert.That(fullList.Count, Is.EqualTo(2)); + Assert.That(withValidManyToOneList.Count, Is.EqualTo(0)); + } + } + [Test] public void EntityJoinWithEntityComparisonInWithClausShouldNotAddJoin() { diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/DotNode.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/DotNode.cs index 0a2d2a1ad61..ab45ae4feeb 100644 --- a/src/NHibernate/Hql/Ast/ANTLR/Tree/DotNode.cs +++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/DotNode.cs @@ -389,7 +389,7 @@ private void DereferenceEntity(EntityType entityType, bool implicitJoin, string //For nullable entity comparisons we always need to add join (like not constrained one-to-one or not-found ignore associations) //NOTE: This fix is not fully correct. It doesn't work for comparisons with null (where e.OneToOneProp is null) // as by default implicit join is generated and to work propelry left join is required (see GH-2611) - bool comparisonWithNullableEntity = false; + bool comparisonWithNullableEntity = entityType.IsNullable && Walker.IsComparativeExpressionClause; if ( IsDotNode( parent ) ) { @@ -398,7 +398,7 @@ private void DereferenceEntity(EntityType entityType, bool implicitJoin, string // entity's PK (because 'our' table would know the FK). parentAsDotNode = ( DotNode ) parent; property = parentAsDotNode._propertyName; - joinIsNeeded = generateJoin && ((Walker.IsSelectStatement && entityType.IsNullable) || !IsReferenceToPrimaryKey( parentAsDotNode._propertyName, entityType )); + joinIsNeeded = generateJoin && ((Walker.IsSelectStatement && comparisonWithNullableEntity) || !IsReferenceToPrimaryKey( parentAsDotNode._propertyName, entityType )); } else if ( ! Walker.IsSelectStatement ) { @@ -411,7 +411,6 @@ private void DereferenceEntity(EntityType entityType, bool implicitJoin, string } else { - comparisonWithNullableEntity = (Walker.IsComparativeExpressionClause && entityType.IsNullable); joinIsNeeded = generateJoin || (Walker.IsInSelect && !Walker.IsInCase) || (Walker.IsInFrom && !Walker.IsComparativeExpressionClause) || comparisonWithNullableEntity; }