Skip to content

Commit b21a978

Browse files
authored
Fix ConditionalProjection GetType from outer query (#2455)
Fixes #2454
1 parent 1fa50d9 commit b21a978

File tree

4 files changed

+282
-15
lines changed

4 files changed

+282
-15
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using NHibernate.Cfg.MappingSchema;
12+
using NHibernate.Criterion;
13+
using NHibernate.Mapping.ByCode;
14+
using NHibernate.SqlCommand;
15+
using NUnit.Framework;
16+
17+
namespace NHibernate.Test.NHSpecificTest.GH2454
18+
{
19+
using System.Threading.Tasks;
20+
[TestFixture]
21+
public class ByCodeFixtureAsync : TestCaseMappingByCode
22+
{
23+
protected override bool AppliesTo(Dialect.Dialect dialect)
24+
{
25+
return dialect.SupportsScalarSubSelects;
26+
}
27+
28+
protected override HbmMapping GetMappings()
29+
{
30+
var mapper = new ModelMapper();
31+
32+
mapper.Class<Project>(rc =>
33+
{
34+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
35+
rc.Property(x => x.Name);
36+
});
37+
38+
mapper.Class<Component>(rc =>
39+
{
40+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
41+
rc.Property(x => x.Name);
42+
rc.ManyToOne(x => x.Project, m => { m.Column("ProjectId"); m.NotNullable(true); });
43+
});
44+
45+
mapper.Class<Tag>(rc =>
46+
{
47+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
48+
rc.Property(x => x.Name);
49+
rc.ManyToOne(x => x.Component1, m => { m.Column("Component1Id"); m.NotNullable(true); });
50+
rc.ManyToOne(x => x.Component2, m => { m.Column("Component2Id"); m.NotNullable(false); });
51+
});
52+
53+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
54+
}
55+
56+
protected override void OnSetUp()
57+
{
58+
using (var session = OpenSession())
59+
using (var transaction = session.BeginTransaction())
60+
{
61+
// alpha entities
62+
var projectAlpha = new Project {Name = "Alpha"};
63+
session.Save(projectAlpha);
64+
65+
var componentAlpha = new Component {Project = projectAlpha, Name = "Thingie"};
66+
session.Save(componentAlpha);
67+
68+
var tagAlpha = new Tag {Component1 = componentAlpha, Name = "A20"};
69+
session.Save(tagAlpha);
70+
71+
// beta entities
72+
var projectBeta = new Project {Name = "Beta"};
73+
session.Save(projectBeta);
74+
75+
var componentBeta = new Component {Project = projectBeta, Name = "Thingie"};
76+
session.Save(componentBeta);
77+
78+
var tagBeta = new Tag {Component1 = componentBeta, Name = "B17"};
79+
session.Save(tagBeta);
80+
81+
transaction.Commit();
82+
}
83+
}
84+
85+
protected override void OnTearDown()
86+
{
87+
using (var session = OpenSession())
88+
using (var transaction = session.BeginTransaction())
89+
{
90+
session.CreateQuery("delete from Tag").ExecuteUpdate();
91+
session.CreateQuery("delete from Component").ExecuteUpdate();
92+
session.CreateQuery("delete from Project").ExecuteUpdate();
93+
transaction.Commit();
94+
}
95+
}
96+
97+
[Test]
98+
public async Task SubqueryCorrelatedThroughConditionalAsync()
99+
{
100+
using (var session = OpenSession())
101+
using (session.BeginTransaction())
102+
{
103+
var tagCriteria = session.CreateCriteria(typeof(Tag), "t");
104+
tagCriteria.CreateCriteria("Component1", "c1");
105+
tagCriteria.CreateCriteria("Component2", "c2", JoinType.LeftOuterJoin);
106+
107+
// create correlated subquery
108+
var projectCriteria = DetachedCriteria.For(typeof(Project), "p");
109+
110+
var conditionalCorrelationProjection = Projections.Conditional(
111+
Restrictions.IsNotNull(Projections.Property("t.Component2")),
112+
Projections.Property("c2.Project"),
113+
Projections.Property("c1.Project"));
114+
projectCriteria.Add(Restrictions.EqProperty("p.Id", conditionalCorrelationProjection));
115+
116+
projectCriteria.SetProjection(Projections.Property("p.Name"));
117+
118+
var projectNameProjection = Projections.SubQuery(projectCriteria);
119+
120+
tagCriteria.Add(Restrictions.Eq(projectNameProjection, "Beta"));
121+
tagCriteria.SetProjection(Projections.Property("t.Name"));
122+
123+
// run query
124+
var results = await (tagCriteria.ListAsync());
125+
126+
Assert.That(results, Is.EquivalentTo(new[] {"B17"}));
127+
}
128+
}
129+
}
130+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
3+
namespace NHibernate.Test.NHSpecificTest.GH2454
4+
{
5+
public class Project
6+
{
7+
public virtual Guid Id { get; set; }
8+
public virtual string Name { get; set; }
9+
}
10+
11+
public class Component
12+
{
13+
public virtual Guid Id { get; set; }
14+
public virtual string Name { get; set; }
15+
public virtual Project Project { get; set; }
16+
}
17+
18+
public class Tag
19+
{
20+
public virtual Guid Id { get; set; }
21+
public virtual string Name { get; set; }
22+
public virtual Component Component1 { get; set; }
23+
public virtual Component Component2 { get; set; }
24+
}
25+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using NHibernate.Cfg.MappingSchema;
2+
using NHibernate.Criterion;
3+
using NHibernate.Mapping.ByCode;
4+
using NHibernate.SqlCommand;
5+
using NUnit.Framework;
6+
7+
namespace NHibernate.Test.NHSpecificTest.GH2454
8+
{
9+
[TestFixture]
10+
public class ByCodeFixture : TestCaseMappingByCode
11+
{
12+
protected override bool AppliesTo(Dialect.Dialect dialect)
13+
{
14+
return dialect.SupportsScalarSubSelects;
15+
}
16+
17+
protected override HbmMapping GetMappings()
18+
{
19+
var mapper = new ModelMapper();
20+
21+
mapper.Class<Project>(rc =>
22+
{
23+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
24+
rc.Property(x => x.Name);
25+
});
26+
27+
mapper.Class<Component>(rc =>
28+
{
29+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
30+
rc.Property(x => x.Name);
31+
rc.ManyToOne(x => x.Project, m => { m.Column("ProjectId"); m.NotNullable(true); });
32+
});
33+
34+
mapper.Class<Tag>(rc =>
35+
{
36+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
37+
rc.Property(x => x.Name);
38+
rc.ManyToOne(x => x.Component1, m => { m.Column("Component1Id"); m.NotNullable(true); });
39+
rc.ManyToOne(x => x.Component2, m => { m.Column("Component2Id"); m.NotNullable(false); });
40+
});
41+
42+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
43+
}
44+
45+
protected override void OnSetUp()
46+
{
47+
using (var session = OpenSession())
48+
using (var transaction = session.BeginTransaction())
49+
{
50+
// alpha entities
51+
var projectAlpha = new Project {Name = "Alpha"};
52+
session.Save(projectAlpha);
53+
54+
var componentAlpha = new Component {Project = projectAlpha, Name = "Thingie"};
55+
session.Save(componentAlpha);
56+
57+
var tagAlpha = new Tag {Component1 = componentAlpha, Name = "A20"};
58+
session.Save(tagAlpha);
59+
60+
// beta entities
61+
var projectBeta = new Project {Name = "Beta"};
62+
session.Save(projectBeta);
63+
64+
var componentBeta = new Component {Project = projectBeta, Name = "Thingie"};
65+
session.Save(componentBeta);
66+
67+
var tagBeta = new Tag {Component1 = componentBeta, Name = "B17"};
68+
session.Save(tagBeta);
69+
70+
transaction.Commit();
71+
}
72+
}
73+
74+
protected override void OnTearDown()
75+
{
76+
using (var session = OpenSession())
77+
using (var transaction = session.BeginTransaction())
78+
{
79+
session.CreateQuery("delete from Tag").ExecuteUpdate();
80+
session.CreateQuery("delete from Component").ExecuteUpdate();
81+
session.CreateQuery("delete from Project").ExecuteUpdate();
82+
transaction.Commit();
83+
}
84+
}
85+
86+
[Test]
87+
public void SubqueryCorrelatedThroughConditional()
88+
{
89+
using (var session = OpenSession())
90+
using (session.BeginTransaction())
91+
{
92+
var tagCriteria = session.CreateCriteria(typeof(Tag), "t");
93+
tagCriteria.CreateCriteria("Component1", "c1");
94+
tagCriteria.CreateCriteria("Component2", "c2", JoinType.LeftOuterJoin);
95+
96+
// create correlated subquery
97+
var projectCriteria = DetachedCriteria.For(typeof(Project), "p");
98+
99+
var conditionalCorrelationProjection = Projections.Conditional(
100+
Restrictions.IsNotNull(Projections.Property("t.Component2")),
101+
Projections.Property("c2.Project"),
102+
Projections.Property("c1.Project"));
103+
projectCriteria.Add(Restrictions.EqProperty("p.Id", conditionalCorrelationProjection));
104+
105+
projectCriteria.SetProjection(Projections.Property("p.Name"));
106+
107+
var projectNameProjection = Projections.SubQuery(projectCriteria);
108+
109+
tagCriteria.Add(Restrictions.Eq(projectNameProjection, "Beta"));
110+
tagCriteria.SetProjection(Projections.Property("t.Name"));
111+
112+
// run query
113+
var results = tagCriteria.List();
114+
115+
Assert.That(results, Is.EquivalentTo(new[] {"B17"}));
116+
}
117+
}
118+
}
119+
}

src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -778,18 +778,8 @@ public IType GetTypeUsingProjection(ICriteria subcriteria, string propertyName)
778778

779779
if (projectionTypes == null)
780780
{
781-
//it does not refer to an alias of a projection,
782-
//look for a property
783-
784-
if (TryGetType(subcriteria, propertyName, out var type))
785-
{
786-
return type;
787-
}
788-
if (outerQueryTranslator != null)
789-
{
790-
return outerQueryTranslator.GetTypeUsingProjection(subcriteria, propertyName);
791-
}
792-
throw new QueryException("Could not find property " + propertyName);
781+
//it does not refer to an alias of a projection, look for a property
782+
return GetType(subcriteria, propertyName);
793783
}
794784
else
795785
{
@@ -804,10 +794,13 @@ public IType GetTypeUsingProjection(ICriteria subcriteria, string propertyName)
804794

805795
public IType GetType(ICriteria subcriteria, string propertyName)
806796
{
807-
if(!TryParseCriteriaPath(subcriteria, propertyName, out var entityName, out var entityPropName, out _))
808-
throw new QueryException("Could not find property " + propertyName);
797+
if (TryGetType(subcriteria, propertyName, out var resultType))
798+
return resultType;
799+
800+
if (outerQueryTranslator != null)
801+
return outerQueryTranslator.GetType(subcriteria, propertyName);
809802

810-
return GetPropertyMapping(entityName).ToType(entityPropName);
803+
throw new QueryException("Could not find property " + propertyName);
811804
}
812805

813806
public bool TryGetType(ICriteria subcriteria, string propertyName, out IType type)

0 commit comments

Comments
 (0)