diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2626/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2626/Fixture.cs new file mode 100644 index 00000000000..c5845f0d37b --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2626/Fixture.cs @@ -0,0 +1,77 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System.Linq; +using NUnit.Framework; +using NHibernate.Linq; + +namespace NHibernate.Test.NHSpecificTest.GH2626 +{ + using System.Threading.Tasks; + [TestFixture] + public class FixtureAsync : BugTestCase + { + protected override void OnSetUp() + { + } + + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + // The HQL delete does all the job inside the database without loading the entities, but it does + // not handle delete order for avoiding violating constraints if any. Use + // session.Delete("from System.Object"); + // instead if in need of having NHibernate ordering the deletes, but this will cause + // loading the entities in the session. + session.CreateQuery("delete from System.Object").ExecuteUpdate(); + + transaction.Commit(); + } + } + + [Test] + public async Task SubqueryWithSelectOnSubclassPropertyAsync() + { + using(var logSpy = new SqlLogSpy()) + using (var session = OpenSession()) + { + var capabilitiesQuery = session + .Query() + .Where(x => x.Name == "aaa") + .Select(x => x.UserId); + + await (session.Query() + .Where(x => capabilitiesQuery.Contains(x.Id)) + .ToListAsync()); + Assert.That(logSpy.GetWholeLog(), Does.Contain("UserId").IgnoreCase); + } + } + + [Test] + public async Task SubqueryWithOfTypeAndSelectOnSubclassPropertyAsync() + { + using(var logSpy = new SqlLogSpy()) + using (var session = OpenSession()) + { + var capabilitiesQuery = session + .Query().OfType() + .Where(x => x.Name == "aaa") + .Select(x => x.UserId); + + await (session.Query() + .Where(x => capabilitiesQuery.Contains(x.Id)) + .ToListAsync()); + Assert.That(logSpy.GetWholeLog(), Does.Contain("UserId").IgnoreCase); + } + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2626/Entity.cs b/src/NHibernate.Test/NHSpecificTest/GH2626/Entity.cs new file mode 100644 index 00000000000..478dac60787 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2626/Entity.cs @@ -0,0 +1,26 @@ +using System; + +namespace NHibernate.Test.NHSpecificTest.GH2626 +{ + abstract class CapabilityAssignment + { + public virtual Guid Id { get; set; } + public virtual string Name { get; set; } + } + + class ApplicationUser + { + public virtual Guid Id { get; set; } + public virtual string UserName { get; set; } + } + + class RoleCapabilityAssignment : CapabilityAssignment + { + public virtual Guid RoleId { get; set; } + } + + class UserCapabilityAssignment : CapabilityAssignment + { + public virtual Guid UserId { get; set; } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2626/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH2626/Fixture.cs new file mode 100644 index 00000000000..c613194c552 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2626/Fixture.cs @@ -0,0 +1,65 @@ +using System.Linq; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH2626 +{ + [TestFixture] + public class Fixture : BugTestCase + { + protected override void OnSetUp() + { + } + + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + // The HQL delete does all the job inside the database without loading the entities, but it does + // not handle delete order for avoiding violating constraints if any. Use + // session.Delete("from System.Object"); + // instead if in need of having NHibernate ordering the deletes, but this will cause + // loading the entities in the session. + session.CreateQuery("delete from System.Object").ExecuteUpdate(); + + transaction.Commit(); + } + } + + [Test] + public void SubqueryWithSelectOnSubclassProperty() + { + using(var logSpy = new SqlLogSpy()) + using (var session = OpenSession()) + { + var capabilitiesQuery = session + .Query() + .Where(x => x.Name == "aaa") + .Select(x => x.UserId); + + session.Query() + .Where(x => capabilitiesQuery.Contains(x.Id)) + .ToList(); + Assert.That(logSpy.GetWholeLog(), Does.Contain("UserId").IgnoreCase); + } + } + + [Test] + public void SubqueryWithOfTypeAndSelectOnSubclassProperty() + { + using(var logSpy = new SqlLogSpy()) + using (var session = OpenSession()) + { + var capabilitiesQuery = session + .Query().OfType() + .Where(x => x.Name == "aaa") + .Select(x => x.UserId); + + session.Query() + .Where(x => capabilitiesQuery.Contains(x.Id)) + .ToList(); + Assert.That(logSpy.GetWholeLog(), Does.Contain("UserId").IgnoreCase); + } + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2626/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH2626/Mappings.hbm.xml new file mode 100644 index 00000000000..cd2fc62f57f --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2626/Mappings.hbm.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NHibernate/Util/ExpressionsHelper.cs b/src/NHibernate/Util/ExpressionsHelper.cs index a5874cf94f0..ee6388a9fa2 100644 --- a/src/NHibernate/Util/ExpressionsHelper.cs +++ b/src/NHibernate/Util/ExpressionsHelper.cs @@ -697,6 +697,12 @@ protected override Expression VisitQuerySourceReference(QuerySourceReferenceExpr { if (node.ReferencedQuerySource is IFromClause fromClause) { + // Types will be different when OfType method is used (e.g. Query().OfType()) + if (fromClause.ItemType != node.Type) + { + _convertType = node.Type; + } + return base.Visit(fromClause.FromExpression); }