Skip to content

Commit e4d3c96

Browse files
bahusoidhazzik
authored andcommitted
Fix Order By for composite property projection in Criteria (#2160)
Fixes #1103
1 parent c79ea1f commit e4d3c96

File tree

5 files changed

+104
-27
lines changed

5 files changed

+104
-27
lines changed

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,5 +367,35 @@ public async Task HqlInClauseSubquery_ForEntityAsync()
367367
Assert.That(results.Count, Is.EqualTo(2));
368368
}
369369
}
370+
371+
//NH-2926 (GH-1103)
372+
[Test]
373+
public async Task QueryOverOrderByAndWhereWithIdProjectionDoesntThrowAsync()
374+
{
375+
// insert the new objects
376+
using (ISession s = OpenSession())
377+
using (ITransaction t = s.BeginTransaction())
378+
{
379+
ClassWithCompositeId theClass = new ClassWithCompositeId(id);
380+
theClass.OneProperty = 5;
381+
382+
ClassWithCompositeId theSecondClass = new ClassWithCompositeId(secondId);
383+
theSecondClass.OneProperty = 10;
384+
385+
await (s.SaveAsync(theClass));
386+
await (s.SaveAsync(theSecondClass));
387+
388+
await (t.CommitAsync());
389+
}
390+
391+
using (ISession s = OpenSession())
392+
{
393+
var results = await (s.QueryOver<ClassWithCompositeId>()
394+
.Select(Projections.Id())
395+
.Where(Restrictions.Eq(Projections.Id(), id))
396+
.OrderBy(Projections.Id()).Desc.ListAsync<Id>());
397+
Assert.That(results.Count, Is.EqualTo(1));
398+
}
399+
}
370400
}
371401
}

src/NHibernate.Test/CompositeId/ClassWithCompositeIdFixture.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,5 +356,35 @@ public void HqlInClauseSubquery_ForEntity()
356356
Assert.That(results.Count, Is.EqualTo(2));
357357
}
358358
}
359+
360+
//NH-2926 (GH-1103)
361+
[Test]
362+
public void QueryOverOrderByAndWhereWithIdProjectionDoesntThrow()
363+
{
364+
// insert the new objects
365+
using (ISession s = OpenSession())
366+
using (ITransaction t = s.BeginTransaction())
367+
{
368+
ClassWithCompositeId theClass = new ClassWithCompositeId(id);
369+
theClass.OneProperty = 5;
370+
371+
ClassWithCompositeId theSecondClass = new ClassWithCompositeId(secondId);
372+
theSecondClass.OneProperty = 10;
373+
374+
s.Save(theClass);
375+
s.Save(theSecondClass);
376+
377+
t.Commit();
378+
}
379+
380+
using (ISession s = OpenSession())
381+
{
382+
var results = s.QueryOver<ClassWithCompositeId>()
383+
.Select(Projections.Id())
384+
.Where(Restrictions.Eq(Projections.Id(), id))
385+
.OrderBy(Projections.Id()).Desc.List<Id>();
386+
Assert.That(results.Count, Is.EqualTo(1));
387+
}
388+
}
359389
}
360390
}

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: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
using System;
2-
using System.Text;
3-
using NHibernate.Criterion;
42
using NHibernate.Engine;
53
using NHibernate.SqlCommand;
4+
using NHibernate.SqlTypes;
65

76
namespace NHibernate.Criterion
87
{
@@ -38,43 +37,54 @@ public Order(string propertyName, bool ascending)
3837
/// </summary>
3938
public virtual SqlString ToSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery)
4039
{
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-
}
47-
48-
string[] columns = criteriaQuery.GetColumnAliasesUsingProjection(criteria, propertyName);
49-
Type.IType type = criteriaQuery.GetTypeUsingProjection(criteria, propertyName);
40+
var columnsOrAliases = GetColumnsOrAliases(criteria, criteriaQuery);
41+
var sqlTypes = ignoreCase ? SqlTypes(criteria, criteriaQuery) : null;
5042

51-
StringBuilder fragment = new StringBuilder();
52-
ISessionFactoryImplementor factory = criteriaQuery.Factory;
53-
for (int i = 0; i < columns.Length; i++)
43+
var fragment = new SqlStringBuilder();
44+
var factory = criteriaQuery.Factory;
45+
for (var i = 0; i < columnsOrAliases.Length; i++)
5446
{
55-
bool lower = ignoreCase && IsStringType(type.SqlTypes(factory)[i]);
56-
47+
var lower = sqlTypes != null && IsStringType(sqlTypes[i]);
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.AddObject(columnsOrAliases[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

71-
if (i < columns.Length - 1)
64+
if (i < columnsOrAliases.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 object[] GetColumnsOrAliases(ICriteria criteria, ICriteriaQuery criteriaQuery)
74+
{
75+
var propName = propertyName ?? (projection as IPropertyProjection)?.PropertyName;
76+
return propName != null
77+
? criteriaQuery.GetColumnAliasesUsingProjection(criteria, propName)
78+
: (object[]) CriterionUtil.GetColumnNamesUsingProjection(projection, criteriaQuery, criteria);
79+
}
80+
81+
private SqlType[] SqlTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
82+
{
83+
var type = projection == null
84+
? criteriaQuery.GetTypeUsingProjection(criteria, propertyName)
85+
: projection.GetTypes(criteria, criteriaQuery)[0];
86+
87+
return type.SqlTypes(criteriaQuery.Factory);
7888
}
7989

8090
public override string ToString()

0 commit comments

Comments
 (0)