diff --git a/src/NHibernate.Test/Async/Criteria/ConditionalProjectionTest.cs b/src/NHibernate.Test/Async/Criteria/ConditionalProjectionTest.cs
new file mode 100644
index 00000000000..39192ae2708
--- /dev/null
+++ b/src/NHibernate.Test/Async/Criteria/ConditionalProjectionTest.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System;
+using System.Linq;
+using NHibernate.Criterion;
+using NUnit.Framework;
+
+namespace NHibernate.Test.Criteria
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class ConditionalProjectionTestAsync : TestCase
+ {
+ protected override string MappingsAssembly => "NHibernate.Test";
+
+ protected override string[] Mappings => new [] {"Criteria.Enrolment.hbm.xml"};
+
+ protected override void OnSetUp()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.Save(new Student {StudentNumber = 6L, Name = "testa"});
+ session.Save(new Student {StudentNumber = 5L, Name = "testz"});
+ session.Save(new Student {StudentNumber = 4L, Name = "test1"});
+ session.Save(new Student {StudentNumber = 3L, Name = "test2"});
+ session.Save(new Student {StudentNumber = 2L, Name = "test998"});
+ session.Save(new Student {StudentNumber = 1L, Name = "test999"});
+ transaction.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var session = Sfi.OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from System.Object").ExecuteUpdate();
+ transaction.Commit();
+ }
+ }
+
+ protected override bool AppliesTo(Dialect.Dialect dialect)
+ {
+ return !TestDialect.HasBrokenTypeInferenceOnSelectedParameters;
+ }
+
+ [Test]
+ public async Task UsingMultiConditionalsAsync()
+ {
+ using (var session = OpenSession())
+ using (session.BeginTransaction())
+ {
+ // when Name = "testa" then 1 ...
+ var orderOfNames = new Tuple[]
+ {
+ System.Tuple.Create("test1", "1"),
+ System.Tuple.Create("testz", "2"),
+ System.Tuple.Create("test2", "3"),
+ System.Tuple.Create("testa", "4")
+ };
+
+ var criterionProjections =
+ orderOfNames
+ .Select(
+ x => new ConditionalProjectionCase(
+ Restrictions.Eq(nameof(Student.Name), x.Item1),
+ Projections.Constant(x.Item2)))
+ .ToArray();
+
+ // ... else 99
+ var elseProjection = Projections.Constant("99");
+
+ var conditionalsProjection = Projections.Conditional(criterionProjections, elseProjection);
+
+ var order = Order.Asc(conditionalsProjection);
+
+ var criteria = session.CreateCriteria(typeof(Student)).AddOrder(order);
+
+ var actuals = await (criteria.ListAsync());
+
+ Assert.That(actuals.Count, Is.GreaterThanOrEqualTo(orderOfNames.Length));
+ for (var i = 0; i < orderOfNames.Length; i++)
+ {
+ var expected = orderOfNames[i];
+ var actual = actuals[i];
+
+ Assert.That(actual.Name, Is.EqualTo(expected.Item1));
+ }
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/Criteria/ConditionalProjectionTest.cs b/src/NHibernate.Test/Criteria/ConditionalProjectionTest.cs
new file mode 100644
index 00000000000..89d4d5e060c
--- /dev/null
+++ b/src/NHibernate.Test/Criteria/ConditionalProjectionTest.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Linq;
+using NHibernate.Criterion;
+using NUnit.Framework;
+
+namespace NHibernate.Test.Criteria
+{
+ [TestFixture]
+ public class ConditionalProjectionTest : TestCase
+ {
+ protected override string MappingsAssembly => "NHibernate.Test";
+
+ protected override string[] Mappings => new [] {"Criteria.Enrolment.hbm.xml"};
+
+ protected override void OnSetUp()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.Save(new Student {StudentNumber = 6L, Name = "testa"});
+ session.Save(new Student {StudentNumber = 5L, Name = "testz"});
+ session.Save(new Student {StudentNumber = 4L, Name = "test1"});
+ session.Save(new Student {StudentNumber = 3L, Name = "test2"});
+ session.Save(new Student {StudentNumber = 2L, Name = "test998"});
+ session.Save(new Student {StudentNumber = 1L, Name = "test999"});
+ transaction.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var session = Sfi.OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from System.Object").ExecuteUpdate();
+ transaction.Commit();
+ }
+ }
+
+ protected override bool AppliesTo(Dialect.Dialect dialect)
+ {
+ return !TestDialect.HasBrokenTypeInferenceOnSelectedParameters;
+ }
+
+ [Test]
+ public void UsingMultiConditionals()
+ {
+ using (var session = OpenSession())
+ using (session.BeginTransaction())
+ {
+ // when Name = "testa" then 1 ...
+ var orderOfNames = new Tuple[]
+ {
+ System.Tuple.Create("test1", "1"),
+ System.Tuple.Create("testz", "2"),
+ System.Tuple.Create("test2", "3"),
+ System.Tuple.Create("testa", "4")
+ };
+
+ var criterionProjections =
+ orderOfNames
+ .Select(
+ x => new ConditionalProjectionCase(
+ Restrictions.Eq(nameof(Student.Name), x.Item1),
+ Projections.Constant(x.Item2)))
+ .ToArray();
+
+ // ... else 99
+ var elseProjection = Projections.Constant("99");
+
+ var conditionalsProjection = Projections.Conditional(criterionProjections, elseProjection);
+
+ var order = Order.Asc(conditionalsProjection);
+
+ var criteria = session.CreateCriteria(typeof(Student)).AddOrder(order);
+
+ var actuals = criteria.List();
+
+ Assert.That(actuals.Count, Is.GreaterThanOrEqualTo(orderOfNames.Length));
+ for (var i = 0; i < orderOfNames.Length; i++)
+ {
+ var expected = orderOfNames[i];
+ var actual = actuals[i];
+
+ Assert.That(actual.Name, Is.EqualTo(expected.Item1));
+ }
+ }
+ }
+ }
+}
diff --git a/src/NHibernate/Criterion/ConditionalProjection.cs b/src/NHibernate/Criterion/ConditionalProjection.cs
index 1ff3a95a185..09cc421638b 100644
--- a/src/NHibernate/Criterion/ConditionalProjection.cs
+++ b/src/NHibernate/Criterion/ConditionalProjection.cs
@@ -1,138 +1,222 @@
+using System;
+using System.Collections.Generic;
+using NHibernate.Engine;
+using NHibernate.SqlCommand;
+using NHibernate.Type;
+
namespace NHibernate.Criterion
{
- using System;
- using System.Collections.Generic;
- using Engine;
- using SqlCommand;
- using Type;
-
+ ///
+ /// Defines a "switch" projection which supports multiple "cases" ("when/then's").
+ ///
+ ///
+ ///
[Serializable]
public class ConditionalProjection : SimpleProjection
{
- private readonly ICriterion criterion;
- private readonly IProjection whenTrue;
- private readonly IProjection whenFalse;
+ private readonly ConditionalProjectionCase[] _cases;
+ private readonly IProjection _elseProjection;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The
+ /// The true
+ /// The else .
public ConditionalProjection(ICriterion criterion, IProjection whenTrue, IProjection whenFalse)
{
- this.whenTrue = whenTrue;
- this.whenFalse = whenFalse;
- this.criterion = criterion;
+ _elseProjection = whenFalse;
+ _cases = new[] {new ConditionalProjectionCase(criterion, whenTrue)};
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The s containing and pairs.
+ /// The else .
+ public ConditionalProjection(ConditionalProjectionCase[] cases, IProjection elseProjection)
+ {
+ if (cases == null)
+ throw new ArgumentNullException(nameof(cases));
+ if (cases.Length == 0)
+ throw new ArgumentException("Array should not be empty.", nameof(cases));
+
+ _cases = cases;
+ _elseProjection = elseProjection;
}
public override bool IsAggregate
{
get
{
- IProjection[] projections = criterion.GetProjections();
- if(projections != null)
+ if (_elseProjection.IsAggregate)
+ return true;
+
+ foreach (var projectionCase in _cases)
{
- foreach (IProjection projection in projections)
+ if (projectionCase.Projection.IsAggregate)
+ return true;
+
+ var projections = projectionCase.Criterion.GetProjections();
+ if (projections != null)
{
- if (projection.IsAggregate)
- return true;
+ foreach (var projection in projections)
+ {
+ if (projection.IsAggregate)
+ {
+ return true;
+ }
+ }
}
}
- if(whenFalse.IsAggregate)
- return true;
- if(whenTrue.IsAggregate)
+
+ return false;
+ }
+ }
+
+ public override bool IsGrouped
+ {
+ get
+ {
+ if (_elseProjection.IsGrouped)
return true;
+
+ foreach (var projectionCase in _cases)
+ {
+ if (projectionCase.Projection.IsGrouped)
+ return true;
+
+ var projections = projectionCase.Criterion.GetProjections();
+ if (projections != null)
+ {
+ foreach (var projection in projections)
+ {
+ if (projection.IsGrouped)
+ return true;
+ }
+ }
+ }
+
return false;
}
}
public override SqlString ToSqlString(ICriteria criteria, int position, ICriteriaQuery criteriaQuery)
{
- SqlString condition = criterion.ToSqlString(criteria, criteriaQuery);
- var ifTrue = CriterionUtil.GetColumnNameAsSqlStringPart(whenTrue, criteriaQuery, criteria);
- var ifFalse = CriterionUtil.GetColumnNameAsSqlStringPart(whenFalse, criteriaQuery, criteria);
- return new SqlString("(case when ", condition, " then ", ifTrue, " else ", ifFalse, " end) as ",
- GetColumnAlias(position));
+ var sqlBuilder = new SqlStringBuilder(5 + (_cases.Length * 4));
+
+ sqlBuilder.Add("(case");
+
+ foreach (var projectionCase in _cases)
+ {
+ sqlBuilder.Add(" when ");
+ sqlBuilder.Add(projectionCase.Criterion.ToSqlString(criteria, criteriaQuery));
+ sqlBuilder.Add(" then ");
+ sqlBuilder.AddObject(CriterionUtil.GetColumnNameAsSqlStringPart(projectionCase.Projection, criteriaQuery, criteria));
+ }
+
+ sqlBuilder.Add(" else ");
+ sqlBuilder.AddObject(CriterionUtil.GetColumnNameAsSqlStringPart(_elseProjection, criteriaQuery, criteria));
+
+ sqlBuilder.Add(" end) as ");
+ sqlBuilder.Add(GetColumnAlias(position));
+
+ return sqlBuilder.ToSqlString();
}
public override IType[] GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
- IType[] trueTypes = whenTrue.GetTypes(criteria, criteriaQuery);
- IType[] falseTypes = whenFalse.GetTypes(criteria, criteriaQuery);
+ var elseTypes = _elseProjection.GetTypes(criteria, criteriaQuery);
- bool areEqual = trueTypes.Length == falseTypes.Length;
- if (areEqual)
+ for (var i = 0; i < _cases.Length; i++)
{
- for (int i = 0; i < trueTypes.Length; i++)
+ var subsequentTypes = _cases[i].Projection.GetTypes(criteria, criteriaQuery);
+ if (!AreTypesEqual(elseTypes, subsequentTypes))
{
- if(trueTypes[i].ReturnedClass != falseTypes[i].ReturnedClass)
- {
- areEqual = false;
- break;
- }
+ string msg = "All projections must return the same types." + Environment.NewLine +
+ "But Else projection returns: [" + string.Join(", ", elseTypes) + "] " + Environment.NewLine +
+ "And When projection " + i + " returns: [" + string.Join(", ", subsequentTypes) + "]";
+
+ throw new HibernateException(msg);
}
}
- if(areEqual == false)
- {
- string msg = "Both true and false projections must return the same types."+ Environment.NewLine +
- "But True projection returns: ["+string.Join(", ", trueTypes) +"] "+ Environment.NewLine+
- "And False projection returns: ["+string.Join(", ", falseTypes)+ "]";
- throw new HibernateException(msg);
+ return elseTypes;
+ }
+
+ public override TypedValue[] GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery)
+ {
+ var typedValues = new List();
+
+ foreach (var projectionCase in _cases)
+ {
+ typedValues.AddRange(projectionCase.Criterion.GetTypedValues(criteria, criteriaQuery));
+ typedValues.AddRange(projectionCase.Projection.GetTypedValues(criteria, criteriaQuery));
}
- return trueTypes;
+ typedValues.AddRange(_elseProjection.GetTypedValues(criteria, criteriaQuery));
+
+ return typedValues.ToArray();
}
- public override TypedValue[] GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery)
+ public override SqlString ToGroupSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
- List tv = new List();
- tv.AddRange(criterion.GetTypedValues(criteria, criteriaQuery));
- tv.AddRange(whenTrue.GetTypedValues(criteria, criteriaQuery));
- tv.AddRange(whenFalse.GetTypedValues(criteria, criteriaQuery));
- return tv.ToArray();
+ var sqlBuilder = new SqlStringBuilder();
+
+ foreach (var projection in _cases)
+ {
+ AddToGroupedSql(sqlBuilder, projection.Criterion.GetProjections(), criteria, criteriaQuery);
+ AddToGroupedSql(sqlBuilder, projection.Projection, criteria, criteriaQuery);
+ }
+
+ AddToGroupedSql(sqlBuilder, _elseProjection, criteria, criteriaQuery);
+
+ // Remove last comma
+ if (sqlBuilder.Count >= 2)
+ {
+ sqlBuilder.RemoveAt(sqlBuilder.Count - 1);
+ }
+
+ return sqlBuilder.ToSqlString();
}
- public override bool IsGrouped
+ private static bool AreTypesEqual(IType[] types1, IType[] types2)
{
- get
+ bool areEqual = types1.Length == types2.Length;
+ if (!areEqual)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < types1.Length; i++)
{
- IProjection[] projections = criterion.GetProjections();
- if(projections != null)
+ if (types1[i].ReturnedClass != types2[i].ReturnedClass)
{
- foreach (IProjection projection in projections)
- {
- if (projection.IsGrouped)
- return true;
- }
+ return false;
}
- if(whenFalse.IsGrouped)
- return true;
- if(whenTrue.IsGrouped)
- return true;
- return false;
}
+
+ return true;
}
- public override SqlString ToGroupSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery)
+ private void AddToGroupedSql(SqlStringBuilder sqlBuilder, IProjection[] projections, ICriteria criteria, ICriteriaQuery criteriaQuery)
{
- SqlStringBuilder buf = new SqlStringBuilder();
- IProjection[] projections = criterion.GetProjections();
- if(projections != null)
+ if (projections == null)
+ return;
+
+ foreach (var projection in projections)
{
- foreach (IProjection proj in projections)
- {
- if (proj.IsGrouped)
- {
- buf.Add(proj.ToGroupSqlString(criteria, criteriaQuery)).Add(", ");
- }
- }
+ AddToGroupedSql(sqlBuilder, projection, criteria, criteriaQuery);
}
- if(whenFalse.IsGrouped)
- buf.Add(whenFalse.ToGroupSqlString(criteria, criteriaQuery)).Add(", ");
- if(whenTrue.IsGrouped)
- buf.Add(whenTrue.ToGroupSqlString(criteria, criteriaQuery)).Add(", ");
+ }
- if(buf.Count >= 2)
+ private void AddToGroupedSql(SqlStringBuilder sqlBuilder, IProjection projection, ICriteria criteria, ICriteriaQuery criteriaQuery)
+ {
+ if (projection.IsGrouped)
{
- buf.RemoveAt(buf.Count - 1);
+ sqlBuilder.Add(projection.ToGroupSqlString(criteria, criteriaQuery));
+ sqlBuilder.Add(", ");
}
- return buf.ToSqlString();
}
}
}
diff --git a/src/NHibernate/Criterion/ConditionalProjectionCase.cs b/src/NHibernate/Criterion/ConditionalProjectionCase.cs
new file mode 100644
index 00000000000..e8fb1aaf9bf
--- /dev/null
+++ b/src/NHibernate/Criterion/ConditionalProjectionCase.cs
@@ -0,0 +1,29 @@
+namespace NHibernate.Criterion
+{
+ ///
+ /// Defines a pair of and .
+ ///
+ public class ConditionalProjectionCase
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The .
+ /// The .
+ public ConditionalProjectionCase(ICriterion criterion, IProjection projection)
+ {
+ Criterion = criterion;
+ Projection = projection;
+ }
+
+ ///
+ /// Gets the .
+ ///
+ public ICriterion Criterion { get; }
+
+ ///
+ /// Gets the .
+ ///
+ public IProjection Projection { get; }
+ }
+}
diff --git a/src/NHibernate/Criterion/Projections.cs b/src/NHibernate/Criterion/Projections.cs
index f152ef9459b..57aba8310f0 100644
--- a/src/NHibernate/Criterion/Projections.cs
+++ b/src/NHibernate/Criterion/Projections.cs
@@ -1,8 +1,8 @@
using System;
using System.Linq.Expressions;
+using NHibernate.Dialect.Function;
using NHibernate.Impl;
using NHibernate.Type;
-using NHibernate.Dialect.Function;
namespace NHibernate.Criterion
{
@@ -317,7 +317,7 @@ public static IProjection Constant(object obj)
///
public static IProjection Constant(object obj, IType type)
{
- return new ConstantProjection(obj,type);
+ return new ConstantProjection(obj, type);
}
///
@@ -327,7 +327,7 @@ public static IProjection Constant(object obj, IType type)
/// The type.
/// The projections.
///
- public static IProjection SqlFunction(string functionName, IType type, params IProjection [] projections)
+ public static IProjection SqlFunction(string functionName, IType type, params IProjection[] projections)
{
return new SqlFunctionProjection(functionName, type, projections);
}
@@ -356,6 +356,18 @@ public static IProjection Conditional(ICriterion criterion, IProjection whenTrue
return new ConditionalProjection(criterion, whenTrue, whenFalse);
}
+ ///
+ /// Conditionally returns one of the s depending on the s of or the .
+ /// This produces an switch-case expression with multiple when-then parts.
+ ///
+ /// The s which contain your s and s.
+ /// The else .
+ /// A for a switch-expression with multiple Criterions ("when") Projections ("then").
+ public static IProjection Conditional(ConditionalProjectionCase[] cases, IProjection elseProjection)
+ {
+ return new ConditionalProjection(cases, elseProjection);
+ }
+
public static IProjection SubQuery(DetachedCriteria detachedCriteria)
{
SelectSubqueryExpression expr = new SelectSubqueryExpression(detachedCriteria);
@@ -417,7 +429,7 @@ public static PropertyProjection Group(Expression> expression
{
return Projections.GroupProperty(ExpressionProcessor.FindMemberExpression(expression.Body));
}
-
+
///
/// A grouping property projection
///
@@ -538,10 +550,10 @@ public static IProjection Select(Expression> expr
internal static IProjection ProcessConcat(MethodCallExpression methodCallExpression)
{
- NewArrayExpression args = (NewArrayExpression)methodCallExpression.Arguments[0];
+ NewArrayExpression args = (NewArrayExpression) methodCallExpression.Arguments[0];
IProjection[] projections = new IProjection[args.Expressions.Count];
- for (var i=0; i