Skip to content

Commit d8dc1b1

Browse files
committed
Fix Order By for composite property projection in Criteria
1 parent 673084b commit d8dc1b1

File tree

5 files changed

+101
-24
lines changed

5 files changed

+101
-24
lines changed

src/NHibernate.Test/Async/CompositeId/ClassWithCompositeIdFixture.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,5 +344,35 @@ public async Task HqlInClauseSubquery_ForEntityAsync()
344344
Assert.That(results.Count, Is.EqualTo(2));
345345
}
346346
}
347+
348+
//NH-2926 (GH-1103)
349+
[Test]
350+
public async Task QueryOverOrderByAndWhereWithIdProjectionDoesntThrowAsync()
351+
{
352+
// insert the new objects
353+
using (ISession s = OpenSession())
354+
using (ITransaction t = s.BeginTransaction())
355+
{
356+
ClassWithCompositeId theClass = new ClassWithCompositeId(id);
357+
theClass.OneProperty = 5;
358+
359+
ClassWithCompositeId theSecondClass = new ClassWithCompositeId(secondId);
360+
theSecondClass.OneProperty = 10;
361+
362+
await (s.SaveAsync(theClass));
363+
await (s.SaveAsync(theSecondClass));
364+
365+
await (t.CommitAsync());
366+
}
367+
368+
using (ISession s = OpenSession())
369+
{
370+
var results = await (s.QueryOver<ClassWithCompositeId>()
371+
.Select(Projections.Id())
372+
.Where(Restrictions.Eq(Projections.Id(), id))
373+
.OrderBy(Projections.Id()).Desc.ListAsync<Id>());
374+
Assert.That(results.Count, Is.EqualTo(1));
375+
}
376+
}
347377
}
348378
}

src/NHibernate.Test/CompositeId/ClassWithCompositeIdFixture.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,5 +333,35 @@ public void HqlInClauseSubquery_ForEntity()
333333
Assert.That(results.Count, Is.EqualTo(2));
334334
}
335335
}
336+
337+
//NH-2926 (GH-1103)
338+
[Test]
339+
public void QueryOverOrderByAndWhereWithIdProjectionDoesntThrow()
340+
{
341+
// insert the new objects
342+
using (ISession s = OpenSession())
343+
using (ITransaction t = s.BeginTransaction())
344+
{
345+
ClassWithCompositeId theClass = new ClassWithCompositeId(id);
346+
theClass.OneProperty = 5;
347+
348+
ClassWithCompositeId theSecondClass = new ClassWithCompositeId(secondId);
349+
theSecondClass.OneProperty = 10;
350+
351+
s.Save(theClass);
352+
s.Save(theSecondClass);
353+
354+
t.Commit();
355+
}
356+
357+
using (ISession s = OpenSession())
358+
{
359+
var results = s.QueryOver<ClassWithCompositeId>()
360+
.Select(Projections.Id())
361+
.Where(Restrictions.Eq(Projections.Id(), id))
362+
.OrderBy(Projections.Id()).Desc.List<Id>();
363+
Assert.That(results.Count, Is.EqualTo(1));
364+
}
365+
}
336366
}
337367
}

src/NHibernate/Criterion/CriterionUtil.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ public static SqlString[] GetColumnNamesForSimpleExpression(
4646

4747
internal static SqlString[] GetColumnNamesUsingProjection(IProjection projection, ICriteriaQuery criteriaQuery, ICriteria criteria)
4848
{
49+
if (projection is IPropertyProjection propertyProjection)
50+
{
51+
return GetColumnNamesUsingPropertyName(criteriaQuery, criteria, propertyProjection.PropertyName);
52+
}
53+
4954
SqlString sqlString = projection.ToSqlString(criteria,
5055
criteriaQuery.GetIndexForAlias(),
5156
criteriaQuery);

src/NHibernate/Criterion/IdentifierProjection.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
using System;
2+
using NHibernate.Persister.Entity;
23
using NHibernate.SqlCommand;
34
using NHibernate.Type;
4-
using NHibernate.Util;
55

66
namespace NHibernate.Criterion
77
{
88
[Serializable]
9-
public class IdentifierProjection : SimpleProjection
9+
public class IdentifierProjection : SimpleProjection, IPropertyProjection
1010
{
1111
private bool grouped;
1212

@@ -21,7 +21,7 @@ protected internal IdentifierProjection() : this(false)
2121

2222
public override string ToString()
2323
{
24-
return "id";
24+
return PropertyName;
2525
}
2626

2727
public override IType[] GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
@@ -66,5 +66,7 @@ public override SqlString ToGroupSqlString(ICriteria criteria, ICriteriaQuery cr
6666
}
6767
return new SqlString(string.Join(",", criteriaQuery.GetIdentifierColumns(criteria)));
6868
}
69+
70+
public string PropertyName => EntityPersister.EntityID;
6971
}
7072
}

src/NHibernate/Criterion/Order.cs

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using System;
2-
using System.Text;
3-
using NHibernate.Criterion;
42
using NHibernate.Engine;
53
using NHibernate.SqlCommand;
64

@@ -38,43 +36,55 @@ public Order(string propertyName, bool ascending)
3836
/// </summary>
3937
public virtual SqlString ToSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery)
4038
{
41-
if (projection != null)
42-
{
43-
SqlString produced = projection.ToSqlString(criteria, 0, criteriaQuery);
44-
SqlString truncated = SqlStringHelper.RemoveAsAliasesFromSql(produced);
45-
return new SqlString(truncated, ascending ? " asc" : " desc");
46-
}
39+
SqlString[] columns = GetColumnsOrAliases(criteria, criteriaQuery);
40+
bool[] toLowerColumns = ignoreCase ? FindStringColumns(criteria, criteriaQuery) : null;
4741

48-
string[] columns = criteriaQuery.GetColumnAliasesUsingProjection(criteria, propertyName);
49-
Type.IType type = criteriaQuery.GetTypeUsingProjection(criteria, propertyName);
50-
51-
StringBuilder fragment = new StringBuilder();
52-
ISessionFactoryImplementor factory = criteriaQuery.Factory;
42+
var fragment = new SqlStringBuilder();
43+
var factory = criteriaQuery.Factory;
5344
for (int i = 0; i < columns.Length; i++)
5445
{
55-
bool lower = ignoreCase && IsStringType(type.SqlTypes(factory)[i]);
46+
bool lower = toLowerColumns?[i] == true;
5647

5748
if (lower)
5849
{
59-
fragment.Append(factory.Dialect.LowercaseFunction)
60-
.Append("(");
50+
fragment
51+
.Add(factory.Dialect.LowercaseFunction)
52+
.Add("(");
6153
}
62-
fragment.Append(columns[i]);
54+
55+
fragment.Add(columns[i]);
6356

6457
if (lower)
6558
{
66-
fragment.Append(")");
59+
fragment.Add(")");
6760
}
6861

69-
fragment.Append(ascending ? " asc" : " desc");
62+
fragment.Add(ascending ? " asc" : " desc");
7063

7164
if (i < columns.Length - 1)
7265
{
73-
fragment.Append(", ");
66+
fragment.Add(", ");
7467
}
7568
}
7669

77-
return new SqlString(fragment.ToString());
70+
return fragment.ToSqlString();
71+
}
72+
73+
private SqlString[] GetColumnsOrAliases(ICriteria criteria, ICriteriaQuery criteriaQuery)
74+
{
75+
var propName = propertyName ?? (projection as IPropertyProjection)?.PropertyName;
76+
return propName != null
77+
? Array.ConvertAll(criteriaQuery.GetColumnAliasesUsingProjection(criteria, propName), x => new SqlString(x))
78+
: CriterionUtil.GetColumnNamesUsingProjection(projection, criteriaQuery, criteria);
79+
}
80+
81+
private bool[] FindStringColumns(ICriteria criteria, ICriteriaQuery criteriaQuery)
82+
{
83+
var type = projection == null
84+
? criteriaQuery.GetTypeUsingProjection(criteria, propertyName)
85+
: projection.GetTypes(criteria, criteriaQuery)[0];
86+
87+
return Array.ConvertAll(type.SqlTypes(criteriaQuery.Factory), t => IsStringType(t));
7888
}
7989

8090
public override string ToString()

0 commit comments

Comments
 (0)