From 73f0c6ce779b9a956704969cc2f6c6ccbc2a278d Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Sat, 16 May 2020 20:27:09 +1200 Subject: [PATCH 1/5] Add support for criteria aliases in SqlProjection and SQLCriterion Fixes #896 --- .../Async/Criteria/CriteriaQueryTest.cs | 86 +++++++++++++++++++ .../Criteria/CriteriaQueryTest.cs | 86 +++++++++++++++++++ src/NHibernate/Criterion/ICriteriaQuery.cs | 21 ++++- src/NHibernate/Criterion/SQLCriterion.cs | 3 +- src/NHibernate/Criterion/SQLProjection.cs | 24 ++++-- .../Criteria/CriteriaQueryTranslator.cs | 14 +++ 6 files changed, 226 insertions(+), 8 deletions(-) diff --git a/src/NHibernate.Test/Async/Criteria/CriteriaQueryTest.cs b/src/NHibernate.Test/Async/Criteria/CriteriaQueryTest.cs index 7e3112b3f94..406409935ee 100644 --- a/src/NHibernate.Test/Async/Criteria/CriteriaQueryTest.cs +++ b/src/NHibernate.Test/Async/Criteria/CriteriaQueryTest.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using NHibernate.Dialect; using NHibernate.Criterion; +using NHibernate.Linq; using NHibernate.SqlCommand; using NHibernate.Transform; using NHibernate.Type; @@ -922,6 +923,91 @@ public async Task ProjectionsTestAsync() s.Close(); } + [Test] + public async Task TestSQLProjectionWithAliasesAsync() + { + using(ISession s = OpenSession()) + using(ITransaction t = s.BeginTransaction()) + { + Course course = new Course(); + course.CourseCode = "HIB"; + course.Description = "Hibernate Training"; + await (s.SaveAsync(course)); + + Student gavin = new Student(); + gavin.Name = "Gavin King"; + gavin.StudentNumber = 667; + await (s.SaveAsync(gavin)); + + Student xam = new Student(); + xam.Name = "Max Rydahl Andersen"; + xam.StudentNumber = 101; + await (s.SaveAsync(xam)); + + Enrolment enrolment = new Enrolment(); + enrolment.Course = course; + enrolment.CourseCode = course.CourseCode; + enrolment.Semester = 1; + enrolment.Year = 1999; + enrolment.Student = xam; + enrolment.StudentNumber = xam.StudentNumber; + xam.Enrolments.Add(enrolment); + await (s.SaveAsync(enrolment)); + + enrolment = new Enrolment(); + enrolment.Course = course; + enrolment.CourseCode = course.CourseCode; + enrolment.Semester = 3; + enrolment.Year = 1998; + enrolment.Student = gavin; + enrolment.StudentNumber = gavin.StudentNumber; + gavin.Enrolments.Add(enrolment); + await (s.SaveAsync(enrolment)); + await (t.CommitAsync()); + } + + using (var s = OpenSession()) + { + Student studentSubquery = null; + var subquery = QueryOver.Of(() => studentSubquery) + .And( + Expression.Sql("{e}.studentId = 667 and {studentSubquery}.studentId = 667")).Select(Projections.Id()); + + var uniqueResult = await (s.CreateCriteria(typeof(Student)) + .Add(Subqueries.Exists(subquery.DetachedCriteria)) + .AddOrder(Order.Asc("Name")) + .CreateCriteria("Enrolments", "e") + .AddOrder(Order.Desc("Year")) + .AddOrder(Order.Desc("Semester")) + .CreateCriteria("Course", "c") + .AddOrder(Order.Asc("Description")) + .SetProjection( + Projections.SqlProjection( + "{alias}.studentId as studentNumber, {e}.Semester as semester," + + " {c}.CourseCode as courseCode, {c}.Description as descr", + new string[] {"studentNumber", "semester", "courseCode", "descr"}, + new[] + { + TypeFactory.HeuristicType(typeof(long)), + TypeFactory.HeuristicType(typeof(short)), + TypeFactory.HeuristicType(typeof(string)), + TypeFactory.HeuristicType(typeof(string)), + })) + .UniqueResultAsync()); + + Assert.That(uniqueResult, Is.Not.Null); + } + + using (var s = OpenSession()) + using (s.BeginTransaction()) + { + await (s.Query().DeleteAsync()); + await (s.Query().DeleteAsync()); + await (s.Query().DeleteAsync()); + await (s.GetCurrentTransaction().CommitAsync()); + } + } + [Test] public async Task CloningProjectionsTestAsync() { diff --git a/src/NHibernate.Test/Criteria/CriteriaQueryTest.cs b/src/NHibernate.Test/Criteria/CriteriaQueryTest.cs index b173b8c6987..847a31dbe0f 100644 --- a/src/NHibernate.Test/Criteria/CriteriaQueryTest.cs +++ b/src/NHibernate.Test/Criteria/CriteriaQueryTest.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using NHibernate.Dialect; using NHibernate.Criterion; +using NHibernate.Linq; using NHibernate.SqlCommand; using NHibernate.Transform; using NHibernate.Type; @@ -1019,6 +1020,91 @@ public void ProjectionsTest() s.Close(); } + [Test] + public void TestSQLProjectionWithAliases() + { + using(ISession s = OpenSession()) + using(ITransaction t = s.BeginTransaction()) + { + Course course = new Course(); + course.CourseCode = "HIB"; + course.Description = "Hibernate Training"; + s.Save(course); + + Student gavin = new Student(); + gavin.Name = "Gavin King"; + gavin.StudentNumber = 667; + s.Save(gavin); + + Student xam = new Student(); + xam.Name = "Max Rydahl Andersen"; + xam.StudentNumber = 101; + s.Save(xam); + + Enrolment enrolment = new Enrolment(); + enrolment.Course = course; + enrolment.CourseCode = course.CourseCode; + enrolment.Semester = 1; + enrolment.Year = 1999; + enrolment.Student = xam; + enrolment.StudentNumber = xam.StudentNumber; + xam.Enrolments.Add(enrolment); + s.Save(enrolment); + + enrolment = new Enrolment(); + enrolment.Course = course; + enrolment.CourseCode = course.CourseCode; + enrolment.Semester = 3; + enrolment.Year = 1998; + enrolment.Student = gavin; + enrolment.StudentNumber = gavin.StudentNumber; + gavin.Enrolments.Add(enrolment); + s.Save(enrolment); + t.Commit(); + } + + using (var s = OpenSession()) + { + Student studentSubquery = null; + var subquery = QueryOver.Of(() => studentSubquery) + .And( + Expression.Sql("{e}.studentId = 667 and {studentSubquery}.studentId = 667")).Select(Projections.Id()); + + var uniqueResult = s.CreateCriteria(typeof(Student)) + .Add(Subqueries.Exists(subquery.DetachedCriteria)) + .AddOrder(Order.Asc("Name")) + .CreateCriteria("Enrolments", "e") + .AddOrder(Order.Desc("Year")) + .AddOrder(Order.Desc("Semester")) + .CreateCriteria("Course", "c") + .AddOrder(Order.Asc("Description")) + .SetProjection( + Projections.SqlProjection( + "{alias}.studentId as studentNumber, {e}.Semester as semester," + + " {c}.CourseCode as courseCode, {c}.Description as descr", + new string[] {"studentNumber", "semester", "courseCode", "descr"}, + new[] + { + TypeFactory.HeuristicType(typeof(long)), + TypeFactory.HeuristicType(typeof(short)), + TypeFactory.HeuristicType(typeof(string)), + TypeFactory.HeuristicType(typeof(string)), + })) + .UniqueResult(); + + Assert.That(uniqueResult, Is.Not.Null); + } + + using (var s = OpenSession()) + using (s.BeginTransaction()) + { + s.Query().Delete(); + s.Query().Delete(); + s.Query().Delete(); + s.GetCurrentTransaction().Commit(); + } + } + [Test] public void CloningProjectionsTest() { diff --git a/src/NHibernate/Criterion/ICriteriaQuery.cs b/src/NHibernate/Criterion/ICriteriaQuery.cs index 7113e6676b4..75331f9a250 100644 --- a/src/NHibernate/Criterion/ICriteriaQuery.cs +++ b/src/NHibernate/Criterion/ICriteriaQuery.cs @@ -1,11 +1,30 @@ using System.Collections.Generic; using NHibernate.Engine; +using NHibernate.Loader.Criteria; using NHibernate.Param; using NHibernate.SqlCommand; using NHibernate.Type; +using NHibernate.Util; namespace NHibernate.Criterion { + //TODO 6.0: Add to ICriteriaQuery interface + internal static class CriteriaQueryExtensions + { + /// + /// Get the criteria alias to SQL alias map + /// + public static IDictionary GetCriteriaSQLAliasMap(this ICriteriaQuery criteriaQuery) + { + if (criteriaQuery is CriteriaQueryTranslator translator) + { + return translator.GetCriteriaSQLAliasMap(); + } + + return CollectionHelper.EmptyDictionary(); + } + } + /// /// An instance of is passed to criterion, /// order and projection instances when actually compiling and @@ -80,4 +99,4 @@ public interface ICriteriaQuery Parameter CreateSkipParameter(int value); Parameter CreateTakeParameter(int value); } -} \ No newline at end of file +} diff --git a/src/NHibernate/Criterion/SQLCriterion.cs b/src/NHibernate/Criterion/SQLCriterion.cs index 06df9db44a3..a73ee7f5344 100644 --- a/src/NHibernate/Criterion/SQLCriterion.cs +++ b/src/NHibernate/Criterion/SQLCriterion.cs @@ -9,6 +9,7 @@ namespace NHibernate.Criterion /// /// An that creates a SQLExpression. /// The string {alias} will be replaced by the alias of the root entity. + /// Criteria aliases can also be used: "{a}.Value + {bc}.Value". /// /// /// This allows for database specific Expressions at the cost of needing to @@ -42,7 +43,7 @@ public override SqlString ToSqlString(ICriteria criteria, ICriteriaQuery criteri parameters[paramPos++].BackTrack = parameter.BackTrack; } } - return _sql.Replace("{alias}", criteriaQuery.GetSQLAlias(criteria)); + return SQLProjection.GetSqlString(criteria, criteriaQuery, _sql); } public override TypedValue[] GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery) diff --git a/src/NHibernate/Criterion/SQLProjection.cs b/src/NHibernate/Criterion/SQLProjection.cs index ce1d9c39448..c8f6f859b61 100644 --- a/src/NHibernate/Criterion/SQLProjection.cs +++ b/src/NHibernate/Criterion/SQLProjection.cs @@ -1,7 +1,6 @@ using System; using NHibernate.SqlCommand; using NHibernate.Type; -using NHibernate.Util; namespace NHibernate.Criterion { @@ -9,6 +8,7 @@ namespace NHibernate.Criterion /// /// A SQL fragment. The string {alias} will be replaced by the alias of the root entity. + /// Criteria aliases can also be used: "{a}.Value + {bc}.Value". /// [Serializable] public sealed class SQLProjection : IProjection @@ -37,15 +37,27 @@ internal SQLProjection(string sql, string groupBy, string[] columnAliases, IType public SqlString ToSqlString(ICriteria criteria, int loc, ICriteriaQuery criteriaQuery) { - //SqlString result = new SqlString(criteriaQuery.GetSQLAlias(criteria)); - //result.Replace(sql, "{alias}"); - //return result; - return new SqlString(sql?.Replace("{alias}", criteriaQuery.GetSQLAlias(criteria))); + return GetSqlString(criteria, criteriaQuery, sql); } public SqlString ToGroupSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery) { - return new SqlString(groupBy?.Replace("{alias}", criteriaQuery.GetSQLAlias(criteria))); + return GetSqlString(criteria, criteriaQuery, groupBy); + } + + private SqlString GetSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery, string sqlTemplate) + { + return GetSqlString(criteria, criteriaQuery, new SqlString(sqlTemplate)); + } + + internal static SqlString GetSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery, SqlString sqlTemplate) + { + foreach (var kvp in criteriaQuery.GetCriteriaSQLAliasMap()) + { + sqlTemplate = sqlTemplate.Replace("{" + kvp.Key + "}", kvp.Value); + } + + return sqlTemplate.Replace("{alias}", criteriaQuery.GetSQLAlias(criteria)); } public override string ToString() diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index 682dc73e084..20954fd5ebb 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -863,6 +863,20 @@ public string GetEntityName(ICriteria subcriteria, string propertyName) return GetEntityName(subcriteria); } + internal IDictionary GetCriteriaSQLAliasMap() + { + var result = criteriaSQLAliasMap.Where(p => !string.IsNullOrEmpty(p.Key.Alias)).ToDictionary(p => p.Key.Alias, p => p.Value); + if (outerQueryTranslator != null) + { + foreach (var p in outerQueryTranslator.GetCriteriaSQLAliasMap()) + { + result.Add(p.Key, p.Value); + } + } + + return result; + } + public string GetSQLAlias(ICriteria criteria, string propertyName) { if (StringHelper.IsNotRoot(propertyName, out var root)) From 931e9f4dbc6f97050e660670cfa0509a5b5eef96 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Mon, 18 May 2020 09:52:12 +1200 Subject: [PATCH 2/5] Account for duplicate aliases on different levels --- .../Async/Criteria/CriteriaQueryTest.cs | 85 +++++++++++++++++++ .../Criteria/CriteriaQueryTest.cs | 85 +++++++++++++++++++ .../Criteria/CriteriaQueryTranslator.cs | 12 ++- 3 files changed, 178 insertions(+), 4 deletions(-) diff --git a/src/NHibernate.Test/Async/Criteria/CriteriaQueryTest.cs b/src/NHibernate.Test/Async/Criteria/CriteriaQueryTest.cs index 406409935ee..b02d678a811 100644 --- a/src/NHibernate.Test/Async/Criteria/CriteriaQueryTest.cs +++ b/src/NHibernate.Test/Async/Criteria/CriteriaQueryTest.cs @@ -1008,6 +1008,91 @@ public async Task TestSQLProjectionWithAliasesAsync() } } + [Test] + public async Task TestSQLProjectionWithDuplicateAliasesAsync() + { + using(ISession s = OpenSession()) + using(ITransaction t = s.BeginTransaction()) + { + Course course = new Course(); + course.CourseCode = "HIB"; + course.Description = "Hibernate Training"; + await (s.SaveAsync(course)); + + Student gavin = new Student(); + gavin.Name = "Gavin King"; + gavin.StudentNumber = 667; + await (s.SaveAsync(gavin)); + + Student xam = new Student(); + xam.Name = "Max Rydahl Andersen"; + xam.StudentNumber = 101; + await (s.SaveAsync(xam)); + + Enrolment enrolment = new Enrolment(); + enrolment.Course = course; + enrolment.CourseCode = course.CourseCode; + enrolment.Semester = 1; + enrolment.Year = 1999; + enrolment.Student = xam; + enrolment.StudentNumber = xam.StudentNumber; + xam.Enrolments.Add(enrolment); + await (s.SaveAsync(enrolment)); + + enrolment = new Enrolment(); + enrolment.Course = course; + enrolment.CourseCode = course.CourseCode; + enrolment.Semester = 3; + enrolment.Year = 1998; + enrolment.Student = gavin; + enrolment.StudentNumber = gavin.StudentNumber; + gavin.Enrolments.Add(enrolment); + await (s.SaveAsync(enrolment)); + await (t.CommitAsync()); + } + + using (var s = OpenSession()) + { + Student student = null; + var subquery = QueryOver.Of(() => student) + .And( + Expression.Sql("{e}.studentId = 667 and {student}.studentId = 667")).Select(Projections.Id()); + + var uniqueResult = await (s.CreateCriteria(typeof(Student), "student") + .Add(Subqueries.Exists(subquery.DetachedCriteria)) + .AddOrder(Order.Asc("Name")) + .CreateCriteria("Enrolments", "e") + .AddOrder(Order.Desc("Year")) + .AddOrder(Order.Desc("Semester")) + .CreateCriteria("Course", "c") + .AddOrder(Order.Asc("Description")) + .SetProjection( + Projections.SqlProjection( + "{alias}.studentId as studentNumber, {e}.Semester as semester," + + " {c}.CourseCode as courseCode, {c}.Description as descr", + new string[] {"studentNumber", "semester", "courseCode", "descr"}, + new[] + { + TypeFactory.HeuristicType(typeof(long)), + TypeFactory.HeuristicType(typeof(short)), + TypeFactory.HeuristicType(typeof(string)), + TypeFactory.HeuristicType(typeof(string)), + })) + .UniqueResultAsync()); + + Assert.That(uniqueResult, Is.Not.Null); + } + + using (var s = OpenSession()) + using (s.BeginTransaction()) + { + await (s.Query().DeleteAsync()); + await (s.Query().DeleteAsync()); + await (s.Query().DeleteAsync()); + await (s.GetCurrentTransaction().CommitAsync()); + } + } + [Test] public async Task CloningProjectionsTestAsync() { diff --git a/src/NHibernate.Test/Criteria/CriteriaQueryTest.cs b/src/NHibernate.Test/Criteria/CriteriaQueryTest.cs index 847a31dbe0f..a072957cb3f 100644 --- a/src/NHibernate.Test/Criteria/CriteriaQueryTest.cs +++ b/src/NHibernate.Test/Criteria/CriteriaQueryTest.cs @@ -1105,6 +1105,91 @@ public void TestSQLProjectionWithAliases() } } + [Test] + public void TestSQLProjectionWithDuplicateAliases() + { + using(ISession s = OpenSession()) + using(ITransaction t = s.BeginTransaction()) + { + Course course = new Course(); + course.CourseCode = "HIB"; + course.Description = "Hibernate Training"; + s.Save(course); + + Student gavin = new Student(); + gavin.Name = "Gavin King"; + gavin.StudentNumber = 667; + s.Save(gavin); + + Student xam = new Student(); + xam.Name = "Max Rydahl Andersen"; + xam.StudentNumber = 101; + s.Save(xam); + + Enrolment enrolment = new Enrolment(); + enrolment.Course = course; + enrolment.CourseCode = course.CourseCode; + enrolment.Semester = 1; + enrolment.Year = 1999; + enrolment.Student = xam; + enrolment.StudentNumber = xam.StudentNumber; + xam.Enrolments.Add(enrolment); + s.Save(enrolment); + + enrolment = new Enrolment(); + enrolment.Course = course; + enrolment.CourseCode = course.CourseCode; + enrolment.Semester = 3; + enrolment.Year = 1998; + enrolment.Student = gavin; + enrolment.StudentNumber = gavin.StudentNumber; + gavin.Enrolments.Add(enrolment); + s.Save(enrolment); + t.Commit(); + } + + using (var s = OpenSession()) + { + Student student = null; + var subquery = QueryOver.Of(() => student) + .And( + Expression.Sql("{e}.studentId = 667 and {student}.studentId = 667")).Select(Projections.Id()); + + var uniqueResult = s.CreateCriteria(typeof(Student), "student") + .Add(Subqueries.Exists(subquery.DetachedCriteria)) + .AddOrder(Order.Asc("Name")) + .CreateCriteria("Enrolments", "e") + .AddOrder(Order.Desc("Year")) + .AddOrder(Order.Desc("Semester")) + .CreateCriteria("Course", "c") + .AddOrder(Order.Asc("Description")) + .SetProjection( + Projections.SqlProjection( + "{alias}.studentId as studentNumber, {e}.Semester as semester," + + " {c}.CourseCode as courseCode, {c}.Description as descr", + new string[] {"studentNumber", "semester", "courseCode", "descr"}, + new[] + { + TypeFactory.HeuristicType(typeof(long)), + TypeFactory.HeuristicType(typeof(short)), + TypeFactory.HeuristicType(typeof(string)), + TypeFactory.HeuristicType(typeof(string)), + })) + .UniqueResult(); + + Assert.That(uniqueResult, Is.Not.Null); + } + + using (var s = OpenSession()) + using (s.BeginTransaction()) + { + s.Query().Delete(); + s.Query().Delete(); + s.Query().Delete(); + s.GetCurrentTransaction().Commit(); + } + } + [Test] public void CloningProjectionsTest() { diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index 20954fd5ebb..0445e29d6e0 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -865,12 +865,16 @@ public string GetEntityName(ICriteria subcriteria, string propertyName) internal IDictionary GetCriteriaSQLAliasMap() { - var result = criteriaSQLAliasMap.Where(p => !string.IsNullOrEmpty(p.Key.Alias)).ToDictionary(p => p.Key.Alias, p => p.Value); - if (outerQueryTranslator != null) + var result = outerQueryTranslator != null + ? new Dictionary(outerQueryTranslator.GetCriteriaSQLAliasMap()) + : new Dictionary(); + + //NOTE: we SHOULD override the outer aliases + foreach (var p in criteriaSQLAliasMap) { - foreach (var p in outerQueryTranslator.GetCriteriaSQLAliasMap()) + if (!string.IsNullOrEmpty(p.Key.Alias)) { - result.Add(p.Key, p.Value); + result[p.Key.Alias] = p.Value; } } From ee5b304f80977c0eb223dddab56e335d51deced2 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Mon, 18 May 2020 10:19:11 +1200 Subject: [PATCH 3/5] Encapsulate logic of rendering SQL aliases into CriteriaQueryTranslator --- src/NHibernate/Criterion/ICriteriaQuery.cs | 9 ++++----- src/NHibernate/Criterion/SQLCriterion.cs | 2 +- src/NHibernate/Criterion/SQLProjection.cs | 12 +----------- .../Criteria/CriteriaQueryTranslator.cs | 19 +++++++++++-------- 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/NHibernate/Criterion/ICriteriaQuery.cs b/src/NHibernate/Criterion/ICriteriaQuery.cs index 75331f9a250..a5714c1623c 100644 --- a/src/NHibernate/Criterion/ICriteriaQuery.cs +++ b/src/NHibernate/Criterion/ICriteriaQuery.cs @@ -4,7 +4,6 @@ using NHibernate.Param; using NHibernate.SqlCommand; using NHibernate.Type; -using NHibernate.Util; namespace NHibernate.Criterion { @@ -12,16 +11,16 @@ namespace NHibernate.Criterion internal static class CriteriaQueryExtensions { /// - /// Get the criteria alias to SQL alias map + /// Substitute the SQL aliases in template. /// - public static IDictionary GetCriteriaSQLAliasMap(this ICriteriaQuery criteriaQuery) + public static SqlString RenderSQLAliases(this ICriteriaQuery criteriaQuery, SqlString sqlTemplate) { if (criteriaQuery is CriteriaQueryTranslator translator) { - return translator.GetCriteriaSQLAliasMap(); + return translator.RenderSQLAliases(sqlTemplate); } - return CollectionHelper.EmptyDictionary(); + return sqlTemplate; } } diff --git a/src/NHibernate/Criterion/SQLCriterion.cs b/src/NHibernate/Criterion/SQLCriterion.cs index a73ee7f5344..093a165cfe4 100644 --- a/src/NHibernate/Criterion/SQLCriterion.cs +++ b/src/NHibernate/Criterion/SQLCriterion.cs @@ -43,7 +43,7 @@ public override SqlString ToSqlString(ICriteria criteria, ICriteriaQuery criteri parameters[paramPos++].BackTrack = parameter.BackTrack; } } - return SQLProjection.GetSqlString(criteria, criteriaQuery, _sql); + return criteriaQuery.RenderSQLAliases(_sql).Replace("{alias}", criteriaQuery.GetSQLAlias(criteria)); } public override TypedValue[] GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery) diff --git a/src/NHibernate/Criterion/SQLProjection.cs b/src/NHibernate/Criterion/SQLProjection.cs index c8f6f859b61..ca538a21b61 100644 --- a/src/NHibernate/Criterion/SQLProjection.cs +++ b/src/NHibernate/Criterion/SQLProjection.cs @@ -47,17 +47,7 @@ public SqlString ToGroupSqlString(ICriteria criteria, ICriteriaQuery criteriaQue private SqlString GetSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery, string sqlTemplate) { - return GetSqlString(criteria, criteriaQuery, new SqlString(sqlTemplate)); - } - - internal static SqlString GetSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery, SqlString sqlTemplate) - { - foreach (var kvp in criteriaQuery.GetCriteriaSQLAliasMap()) - { - sqlTemplate = sqlTemplate.Replace("{" + kvp.Key + "}", kvp.Value); - } - - return sqlTemplate.Replace("{alias}", criteriaQuery.GetSQLAlias(criteria)); + return criteriaQuery.RenderSQLAliases(new SqlString(sqlTemplate)).Replace("{alias}", criteriaQuery.GetSQLAlias(criteria)); } public override string ToString() diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index 0445e29d6e0..1417610a117 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -863,22 +863,25 @@ public string GetEntityName(ICriteria subcriteria, string propertyName) return GetEntityName(subcriteria); } - internal IDictionary GetCriteriaSQLAliasMap() + /// + /// Substitute the SQL aliases in template. + /// + public SqlString RenderSQLAliases(SqlString sqlTemplate) { - var result = outerQueryTranslator != null - ? new Dictionary(outerQueryTranslator.GetCriteriaSQLAliasMap()) - : new Dictionary(); - - //NOTE: we SHOULD override the outer aliases foreach (var p in criteriaSQLAliasMap) { if (!string.IsNullOrEmpty(p.Key.Alias)) { - result[p.Key.Alias] = p.Value; + sqlTemplate = sqlTemplate.Replace("{" + p.Key.Alias + "}", p.Value); } } - return result; + if (outerQueryTranslator != null) + { + return outerQueryTranslator.RenderSQLAliases(sqlTemplate); + } + + return sqlTemplate; } public string GetSQLAlias(ICriteria criteria, string propertyName) From eed9c8c5be9f5a69e54309ff6399f3ed88009983 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Mon, 18 May 2020 10:30:05 +1200 Subject: [PATCH 4/5] Linqfy --- .../Loader/Criteria/CriteriaQueryTranslator.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index 1417610a117..4fc578cbb38 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -868,13 +868,9 @@ public string GetEntityName(ICriteria subcriteria, string propertyName) /// public SqlString RenderSQLAliases(SqlString sqlTemplate) { - foreach (var p in criteriaSQLAliasMap) - { - if (!string.IsNullOrEmpty(p.Key.Alias)) - { - sqlTemplate = sqlTemplate.Replace("{" + p.Key.Alias + "}", p.Value); - } - } + sqlTemplate = criteriaSQLAliasMap + .Where(p => !string.IsNullOrEmpty(p.Key.Alias)) + .Aggregate(sqlTemplate, (current, p) => current.Replace("{" + p.Key.Alias + "}", p.Value)); if (outerQueryTranslator != null) { From fc7e36953c1b6ca3fafca62df5bfc4a04a0606a3 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Mon, 18 May 2020 10:30:47 +1200 Subject: [PATCH 5/5] Avoid modifying parameter --- src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs index 4fc578cbb38..5f315951413 100644 --- a/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs +++ b/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs @@ -868,16 +868,16 @@ public string GetEntityName(ICriteria subcriteria, string propertyName) /// public SqlString RenderSQLAliases(SqlString sqlTemplate) { - sqlTemplate = criteriaSQLAliasMap + var result = criteriaSQLAliasMap .Where(p => !string.IsNullOrEmpty(p.Key.Alias)) .Aggregate(sqlTemplate, (current, p) => current.Replace("{" + p.Key.Alias + "}", p.Value)); if (outerQueryTranslator != null) { - return outerQueryTranslator.RenderSQLAliases(sqlTemplate); + return outerQueryTranslator.RenderSQLAliases(result); } - return sqlTemplate; + return result; } public string GetSQLAlias(ICriteria criteria, string propertyName)