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)