Skip to content

Commit 3e59d7d

Browse files
committed
Support calculations on both sides of boolean expression
1 parent 011f678 commit 3e59d7d

File tree

4 files changed

+53
-41
lines changed

4 files changed

+53
-41
lines changed

src/NHibernate.Test/Async/Criteria/Lambda/IntegrationFixture.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,11 +529,20 @@ public async Task QueryOverArithmeticAsync()
529529
var persons3 = await (s.QueryOver<Person>().WhereRestrictionOn(p => (p.Age * 2) / 2 + 20 - 20).IsBetween(19).And(21).ListAsync());
530530
var persons4 = await (s.QueryOver<Person>().WhereRestrictionOn(p => -(-p.Age)).IsBetween(19).And(21).ListAsync());
531531
var persons5 = await (s.QueryOver<Person>().WhereRestrictionOn(p => (p.Age * 2) / 2 + 20 - 20).IsBetween(19).And(51).ListAsync());
532+
var persons6 = await (s.QueryOver<Person>().Where(p => (p.Age * 2) / 2 + 20 - 20 == p.Age - p.Age + 20).ListAsync());
533+
#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'
534+
var persons7 = await (s.QueryOver<Person>().Where(p => (p.Age * 2) / 2 + 20 - 20 == null || p.Age * 2 == 20 * 1).ListAsync());
535+
#pragma warning restore CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'
536+
var val1 = await (s.QueryOver<Person>().Select(p => p.Age *2).Where( p => p.Age == 20).SingleOrDefaultAsync<int>());
537+
532538
Assert.That(persons1.Count, Is.EqualTo(1));
533539
Assert.That(persons2.Count, Is.EqualTo(1));
534540
Assert.That(persons3.Count, Is.EqualTo(1));
535541
Assert.That(persons4.Count, Is.EqualTo(1));
536542
Assert.That(persons5.Count, Is.EqualTo(2));
543+
Assert.That(persons6.Count, Is.EqualTo(1));
544+
Assert.That(persons7.Count, Is.EqualTo(0));
545+
Assert.That(val1, Is.EqualTo(40));
537546
}
538547
}
539548
}

src/NHibernate.Test/Criteria/Lambda/IntegrationFixture.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,11 +517,20 @@ public void QueryOverArithmetic()
517517
var persons3 = s.QueryOver<Person>().WhereRestrictionOn(p => (p.Age * 2) / 2 + 20 - 20).IsBetween(19).And(21).List();
518518
var persons4 = s.QueryOver<Person>().WhereRestrictionOn(p => -(-p.Age)).IsBetween(19).And(21).List();
519519
var persons5 = s.QueryOver<Person>().WhereRestrictionOn(p => (p.Age * 2) / 2 + 20 - 20).IsBetween(19).And(51).List();
520+
var persons6 = s.QueryOver<Person>().Where(p => (p.Age * 2) / 2 + 20 - 20 == p.Age - p.Age + 20).List();
521+
#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'
522+
var persons7 = s.QueryOver<Person>().Where(p => (p.Age * 2) / 2 + 20 - 20 == null || p.Age * 2 == 20 * 1).List();
523+
#pragma warning restore CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'
524+
var val1 = s.QueryOver<Person>().Select(p => p.Age *2).Where( p => p.Age == 20).SingleOrDefault<int>();
525+
520526
Assert.That(persons1.Count, Is.EqualTo(1));
521527
Assert.That(persons2.Count, Is.EqualTo(1));
522528
Assert.That(persons3.Count, Is.EqualTo(1));
523529
Assert.That(persons4.Count, Is.EqualTo(1));
524530
Assert.That(persons5.Count, Is.EqualTo(2));
531+
Assert.That(persons6.Count, Is.EqualTo(1));
532+
Assert.That(persons7.Count, Is.EqualTo(0));
533+
Assert.That(val1, Is.EqualTo(40));
525534
}
526535
}
527536
}

src/NHibernate/Criterion/ConstantProjection.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace NHibernate.Criterion
1313
public class ConstantProjection : SimpleProjection
1414
{
1515
private readonly object value;
16-
private readonly TypedValue typedValue;
16+
public TypedValue TypedValue { get; }
1717

1818
public ConstantProjection(object value) : this(value, NHibernateUtil.GuessType(value.GetType()))
1919
{
@@ -22,7 +22,7 @@ public ConstantProjection(object value) : this(value, NHibernateUtil.GuessType(v
2222
public ConstantProjection(object value, IType type)
2323
{
2424
this.value = value;
25-
typedValue = new TypedValue(type, this.value);
25+
TypedValue = new TypedValue(type, this.value);
2626
}
2727

2828
public override bool IsAggregate
@@ -43,19 +43,19 @@ public override bool IsGrouped
4343
public override SqlString ToSqlString(ICriteria criteria, int position, ICriteriaQuery criteriaQuery)
4444
{
4545
return new SqlString(
46-
criteriaQuery.NewQueryParameter(typedValue).Single(),
46+
criteriaQuery.NewQueryParameter(TypedValue).Single(),
4747
" as ",
4848
GetColumnAliases(position, criteria, criteriaQuery)[0]);
4949
}
5050

5151
public override IType[] GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
5252
{
53-
return new IType[] { typedValue.Type };
53+
return new IType[] { TypedValue.Type };
5454
}
5555

5656
public override TypedValue[] GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery)
5757
{
58-
return new TypedValue[] { typedValue };
58+
return new TypedValue[] { TypedValue };
5959
}
6060
}
6161
}

src/NHibernate/Impl/ExpressionProcessor.cs

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Text.RegularExpressions;
88
using NHibernate.Criterion;
99
using NHibernate.Dialect.Function;
10+
using NHibernate.Engine;
1011
using NHibernate.Type;
1112
using NHibernate.Util;
1213
using Expression = System.Linq.Expressions.Expression;
@@ -89,16 +90,18 @@ public Order CreateOrder(Func<string, Order> orderStringDelegate, Func<IProjecti
8990

9091
/// <summary>
9192
/// Retrieve the property name from a supplied PropertyProjection
92-
/// Note: throws if the supplied IProjection is not a PropertyProjection
93+
/// Note: throws if the supplied IProjection is not a IPropertyProjection
9394
/// </summary>
9495
public string AsProperty()
9596
{
9697
if (_property != null) return _property;
9798

98-
var propertyProjection = _projection as PropertyProjection;
99+
var propertyProjection = _projection as IPropertyProjection;
99100
if (propertyProjection == null) throw new InvalidOperationException("Cannot determine property for " + _projection);
100101
return propertyProjection.PropertyName;
101102
}
103+
104+
internal bool IsConstant(out ConstantProjection value) => (value = _projection as ConstantProjection) != null;
102105
}
103106

104107
private static readonly Dictionary<ExpressionType, Func<ProjectionInfo, object, ICriterion>> _simpleExpressionCreators;
@@ -263,7 +266,8 @@ public static object FindValue(Expression expression)
263266
public static ProjectionInfo FindMemberProjection(Expression expression)
264267
{
265268
if (!IsMemberExpression(expression))
266-
return AsArithmeticExpression(expression) ?? ProjectionInfo.ForProjection(Projections.Constant(FindValue(expression)));
269+
return AsArithmeticProjection(expression)
270+
?? ProjectionInfo.ForProjection(Projections.Constant(FindValue(expression), NHibernateUtil.GuessType(expression.Type)));
267271

268272
var unwrapExpression = UnwrapConvertExpression(expression);
269273
if (unwrapExpression != null)
@@ -281,14 +285,14 @@ public static ProjectionInfo FindMemberProjection(Expression expression)
281285
return ProjectionInfo.ForProjection(processor(methodCallExpression));
282286
}
283287
}
284-
var memberExpression = expression as MemberExpression;
285-
if (memberExpression != null)
288+
var memberExpression = expression as MemberExpression;
289+
if (memberExpression != null)
286290
{
287-
var signature = Signature(memberExpression.Member);
291+
var signature = Signature(memberExpression.Member);
288292
Func<Expression, IProjection> processor;
289293
if (_customProjectionProcessors.TryGetValue(signature, out processor))
290294
{
291-
return ProjectionInfo.ForProjection(processor(memberExpression));
295+
return ProjectionInfo.ForProjection(processor(memberExpression));
292296
}
293297
}
294298

@@ -317,7 +321,7 @@ private static bool IsSupportedUnaryExpression(UnaryExpression expression)
317321
return expression.NodeType == ExpressionType.Negate;
318322
}
319323

320-
private static ProjectionInfo AsArithmeticExpression(Expression expression)
324+
private static ProjectionInfo AsArithmeticProjection(Expression expression)
321325
{
322326
if (!(expression is BinaryExpression be))
323327
{
@@ -328,7 +332,7 @@ private static ProjectionInfo AsArithmeticExpression(Expression expression)
328332
}
329333

330334
var unwrapExpression = UnwrapConvertExpression(expression);
331-
return unwrapExpression != null ? AsArithmeticExpression(unwrapExpression) : null;
335+
return unwrapExpression != null ? AsArithmeticProjection(unwrapExpression) : null;
332336
}
333337

334338
if (!_binaryArithmethicTemplates.TryGetValue(be.NodeType, out var template))
@@ -565,21 +569,12 @@ private static object ConvertType(object value, System.Type type)
565569
throw new ArgumentException(string.Format("Cannot convert '{0}' to {1}", value, type));
566570
}
567571

568-
private static ICriterion ProcessSimpleExpression(BinaryExpression be)
569-
{
570-
if (be.Left.NodeType == ExpressionType.Call && ((MethodCallExpression)be.Left).Method.Name == "CompareString")
571-
return ProcessVisualBasicStringComparison(be);
572-
573-
return ProcessSimpleExpression(be.Left, be.Right, be.NodeType);
574-
}
575-
576-
private static ICriterion ProcessSimpleExpression(Expression left, Expression right, ExpressionType nodeType)
572+
private static ICriterion ProcessSimpleExpression(Expression left, TypedValue rightValue, ExpressionType nodeType)
577573
{
578574
ProjectionInfo property = FindMemberProjection(left);
579575
System.Type propertyType = FindMemberType(left);
580576

581-
object value = FindValue(right);
582-
value = ConvertType(value, propertyType);
577+
var value = ConvertType(rightValue.Value, propertyType);
583578

584579
if (value == null)
585580
return ProcessSimpleNullExpression(property, nodeType);
@@ -591,14 +586,17 @@ private static ICriterion ProcessSimpleExpression(Expression left, Expression ri
591586
return simpleExpressionCreator(property, value);
592587
}
593588

594-
private static ICriterion ProcessVisualBasicStringComparison(BinaryExpression be)
589+
private static ICriterion ProcessAsVisualBasicStringComparison(Expression left, ExpressionType nodeType)
595590
{
596-
var methodCall = (MethodCallExpression)be.Left;
591+
if (left.NodeType != ExpressionType.Call)
592+
{
593+
return null;
594+
}
597595

598-
if (IsMemberExpression(methodCall.Arguments[1]))
599-
return ProcessMemberExpression(methodCall.Arguments[0], methodCall.Arguments[1], be.NodeType);
600-
else
601-
return ProcessSimpleExpression(methodCall.Arguments[0], methodCall.Arguments[1], be.NodeType);
596+
var methodCall = (MethodCallExpression) left;
597+
return methodCall.Method.Name == "CompareString"
598+
? ProcessMemberExpression(methodCall.Arguments[0], methodCall.Arguments[1], nodeType)
599+
: null;
602600
}
603601

604602
private static ICriterion ProcessSimpleNullExpression(ProjectionInfo property, ExpressionType expressionType)
@@ -613,15 +611,15 @@ private static ICriterion ProcessSimpleNullExpression(ProjectionInfo property, E
613611
throw new ArgumentException("Cannot supply null value to operator " + expressionType, nameof(expressionType));
614612
}
615613

616-
private static ICriterion ProcessMemberExpression(BinaryExpression be)
617-
{
618-
return ProcessMemberExpression(be.Left, be.Right, be.NodeType);
619-
}
620-
621614
private static ICriterion ProcessMemberExpression(Expression left, Expression right, ExpressionType nodeType)
622615
{
623616
ProjectionInfo leftProperty = FindMemberProjection(left);
624617
ProjectionInfo rightProperty = FindMemberProjection(right);
618+
if (rightProperty.IsConstant(out var constProjection))
619+
{
620+
return ProcessAsVisualBasicStringComparison(left, nodeType)
621+
?? ProcessSimpleExpression(left, constProjection.TypedValue, nodeType);
622+
}
625623

626624
Func<ProjectionInfo, ProjectionInfo, ICriterion> propertyExpressionCreator;
627625
if (!_propertyExpressionCreators.TryGetValue(nodeType, out propertyExpressionCreator))
@@ -660,11 +658,7 @@ private static ICriterion ProcessBinaryExpression(BinaryExpression expression)
660658
case ExpressionType.GreaterThanOrEqual:
661659
case ExpressionType.LessThan:
662660
case ExpressionType.LessThanOrEqual:
663-
if (IsMemberExpression(expression.Right))
664-
return ProcessMemberExpression(expression);
665-
else
666-
return ProcessSimpleExpression(expression);
667-
661+
return ProcessMemberExpression(expression.Left, expression.Right, expression.NodeType);
668662
default:
669663
throw new NotImplementedException("Unhandled binary expression: " + expression.NodeType + ", " + expression);
670664
}

0 commit comments

Comments
 (0)