diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2454/FixtureByCode.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2454/FixtureByCode.cs
new file mode 100644
index 00000000000..11e91807118
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2454/FixtureByCode.cs
@@ -0,0 +1,130 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using NHibernate.Cfg.MappingSchema;
+using NHibernate.Criterion;
+using NHibernate.Mapping.ByCode;
+using NHibernate.SqlCommand;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH2454
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class ByCodeFixtureAsync : TestCaseMappingByCode
+ {
+ protected override bool AppliesTo(Dialect.Dialect dialect)
+ {
+ return dialect.SupportsScalarSubSelects;
+ }
+
+ protected override HbmMapping GetMappings()
+ {
+ var mapper = new ModelMapper();
+
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
+ rc.Property(x => x.Name);
+ });
+
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
+ rc.Property(x => x.Name);
+ rc.ManyToOne(x => x.Project, m => { m.Column("ProjectId"); m.NotNullable(true); });
+ });
+
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
+ rc.Property(x => x.Name);
+ rc.ManyToOne(x => x.Component1, m => { m.Column("Component1Id"); m.NotNullable(true); });
+ rc.ManyToOne(x => x.Component2, m => { m.Column("Component2Id"); m.NotNullable(false); });
+ });
+
+ return mapper.CompileMappingForAllExplicitlyAddedEntities();
+ }
+
+ protected override void OnSetUp()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ // alpha entities
+ var projectAlpha = new Project {Name = "Alpha"};
+ session.Save(projectAlpha);
+
+ var componentAlpha = new Component {Project = projectAlpha, Name = "Thingie"};
+ session.Save(componentAlpha);
+
+ var tagAlpha = new Tag {Component1 = componentAlpha, Name = "A20"};
+ session.Save(tagAlpha);
+
+ // beta entities
+ var projectBeta = new Project {Name = "Beta"};
+ session.Save(projectBeta);
+
+ var componentBeta = new Component {Project = projectBeta, Name = "Thingie"};
+ session.Save(componentBeta);
+
+ var tagBeta = new Tag {Component1 = componentBeta, Name = "B17"};
+ session.Save(tagBeta);
+
+ transaction.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from Tag").ExecuteUpdate();
+ session.CreateQuery("delete from Component").ExecuteUpdate();
+ session.CreateQuery("delete from Project").ExecuteUpdate();
+ transaction.Commit();
+ }
+ }
+
+ [Test]
+ public async Task SubqueryCorrelatedThroughConditionalAsync()
+ {
+ using (var session = OpenSession())
+ using (session.BeginTransaction())
+ {
+ var tagCriteria = session.CreateCriteria(typeof(Tag), "t");
+ tagCriteria.CreateCriteria("Component1", "c1");
+ tagCriteria.CreateCriteria("Component2", "c2", JoinType.LeftOuterJoin);
+
+ // create correlated subquery
+ var projectCriteria = DetachedCriteria.For(typeof(Project), "p");
+
+ var conditionalCorrelationProjection = Projections.Conditional(
+ Restrictions.IsNotNull(Projections.Property("t.Component2")),
+ Projections.Property("c2.Project"),
+ Projections.Property("c1.Project"));
+ projectCriteria.Add(Restrictions.EqProperty("p.Id", conditionalCorrelationProjection));
+
+ projectCriteria.SetProjection(Projections.Property("p.Name"));
+
+ var projectNameProjection = Projections.SubQuery(projectCriteria);
+
+ tagCriteria.Add(Restrictions.Eq(projectNameProjection, "Beta"));
+ tagCriteria.SetProjection(Projections.Property("t.Name"));
+
+ // run query
+ var results = await (tagCriteria.ListAsync());
+
+ Assert.That(results, Is.EquivalentTo(new[] {"B17"}));
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH2454/Domain.cs b/src/NHibernate.Test/NHSpecificTest/GH2454/Domain.cs
new file mode 100644
index 00000000000..48fe46e2b5c
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH2454/Domain.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace NHibernate.Test.NHSpecificTest.GH2454
+{
+ public class Project
+ {
+ public virtual Guid Id { get; set; }
+ public virtual string Name { get; set; }
+ }
+
+ public class Component
+ {
+ public virtual Guid Id { get; set; }
+ public virtual string Name { get; set; }
+ public virtual Project Project { get; set; }
+ }
+
+ public class Tag
+ {
+ public virtual Guid Id { get; set; }
+ public virtual string Name { get; set; }
+ public virtual Component Component1 { get; set; }
+ public virtual Component Component2 { get; set; }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH2454/FixtureByCode.cs b/src/NHibernate.Test/NHSpecificTest/GH2454/FixtureByCode.cs
new file mode 100644
index 00000000000..30a93637682
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH2454/FixtureByCode.cs
@@ -0,0 +1,119 @@
+using NHibernate.Cfg.MappingSchema;
+using NHibernate.Criterion;
+using NHibernate.Mapping.ByCode;
+using NHibernate.SqlCommand;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH2454
+{
+ [TestFixture]
+ public class ByCodeFixture : TestCaseMappingByCode
+ {
+ protected override bool AppliesTo(Dialect.Dialect dialect)
+ {
+ return dialect.SupportsScalarSubSelects;
+ }
+
+ protected override HbmMapping GetMappings()
+ {
+ var mapper = new ModelMapper();
+
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
+ rc.Property(x => x.Name);
+ });
+
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
+ rc.Property(x => x.Name);
+ rc.ManyToOne(x => x.Project, m => { m.Column("ProjectId"); m.NotNullable(true); });
+ });
+
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
+ rc.Property(x => x.Name);
+ rc.ManyToOne(x => x.Component1, m => { m.Column("Component1Id"); m.NotNullable(true); });
+ rc.ManyToOne(x => x.Component2, m => { m.Column("Component2Id"); m.NotNullable(false); });
+ });
+
+ return mapper.CompileMappingForAllExplicitlyAddedEntities();
+ }
+
+ protected override void OnSetUp()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ // alpha entities
+ var projectAlpha = new Project {Name = "Alpha"};
+ session.Save(projectAlpha);
+
+ var componentAlpha = new Component {Project = projectAlpha, Name = "Thingie"};
+ session.Save(componentAlpha);
+
+ var tagAlpha = new Tag {Component1 = componentAlpha, Name = "A20"};
+ session.Save(tagAlpha);
+
+ // beta entities
+ var projectBeta = new Project {Name = "Beta"};
+ session.Save(projectBeta);
+
+ var componentBeta = new Component {Project = projectBeta, Name = "Thingie"};
+ session.Save(componentBeta);
+
+ var tagBeta = new Tag {Component1 = componentBeta, Name = "B17"};
+ session.Save(tagBeta);
+
+ transaction.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from Tag").ExecuteUpdate();
+ session.CreateQuery("delete from Component").ExecuteUpdate();
+ session.CreateQuery("delete from Project").ExecuteUpdate();
+ transaction.Commit();
+ }
+ }
+
+ [Test]
+ public void SubqueryCorrelatedThroughConditional()
+ {
+ using (var session = OpenSession())
+ using (session.BeginTransaction())
+ {
+ var tagCriteria = session.CreateCriteria(typeof(Tag), "t");
+ tagCriteria.CreateCriteria("Component1", "c1");
+ tagCriteria.CreateCriteria("Component2", "c2", JoinType.LeftOuterJoin);
+
+ // create correlated subquery
+ var projectCriteria = DetachedCriteria.For(typeof(Project), "p");
+
+ var conditionalCorrelationProjection = Projections.Conditional(
+ Restrictions.IsNotNull(Projections.Property("t.Component2")),
+ Projections.Property("c2.Project"),
+ Projections.Property("c1.Project"));
+ projectCriteria.Add(Restrictions.EqProperty("p.Id", conditionalCorrelationProjection));
+
+ projectCriteria.SetProjection(Projections.Property("p.Name"));
+
+ var projectNameProjection = Projections.SubQuery(projectCriteria);
+
+ tagCriteria.Add(Restrictions.Eq(projectNameProjection, "Beta"));
+ tagCriteria.SetProjection(Projections.Property("t.Name"));
+
+ // run query
+ var results = tagCriteria.List();
+
+ Assert.That(results, Is.EquivalentTo(new[] {"B17"}));
+ }
+ }
+ }
+}
diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs
index 5f315951413..955c4802dd3 100644
--- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs
+++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs
@@ -778,18 +778,8 @@ public IType GetTypeUsingProjection(ICriteria subcriteria, string propertyName)
if (projectionTypes == null)
{
- //it does not refer to an alias of a projection,
- //look for a property
-
- if (TryGetType(subcriteria, propertyName, out var type))
- {
- return type;
- }
- if (outerQueryTranslator != null)
- {
- return outerQueryTranslator.GetTypeUsingProjection(subcriteria, propertyName);
- }
- throw new QueryException("Could not find property " + propertyName);
+ //it does not refer to an alias of a projection, look for a property
+ return GetType(subcriteria, propertyName);
}
else
{
@@ -804,10 +794,13 @@ public IType GetTypeUsingProjection(ICriteria subcriteria, string propertyName)
public IType GetType(ICriteria subcriteria, string propertyName)
{
- if(!TryParseCriteriaPath(subcriteria, propertyName, out var entityName, out var entityPropName, out _))
- throw new QueryException("Could not find property " + propertyName);
+ if (TryGetType(subcriteria, propertyName, out var resultType))
+ return resultType;
+
+ if (outerQueryTranslator != null)
+ return outerQueryTranslator.GetType(subcriteria, propertyName);
- return GetPropertyMapping(entityName).ToType(entityPropName);
+ throw new QueryException("Could not find property " + propertyName);
}
public bool TryGetType(ICriteria subcriteria, string propertyName, out IType type)