Skip to content

Improves support for enum constants within queries #412

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ChangeLog/7.1.3_dev.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[main] Addressed race condition issue with TranslatorState.NonVisitableExpressions
[main] Improved working with nullable enum constants in queries
[postgresql] Improved database structucture extraction
[postgresql] Addressed certain cases of decimal results of aggregate operations being unable to fit to .NET decimal
186 changes: 186 additions & 0 deletions Orm/Xtensive.Orm.Tests.Core/Linq/EnumRewriterTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
using System;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Text;
using Xtensive.Reflection;
using Xtensive.Orm.Linq.Expressions.Visitors;
using NUnit.Framework;
using System.Linq;

namespace Xtensive.Orm.Tests.Core.Linq
{
public class EnumRewriterTest
{
private enum ByteBasedEnum : byte
{
Value1 = 1, Value2, Value3
}

private enum SByteBasedEnum : sbyte
{
Value1 = 1, Value2, Value3
}

private enum ShortBasedEnum : short
{
Value1 = 1, Value2, Value3
}

private enum UShortBasedEnum : ushort
{
Value1 = 1, Value2, Value3
}

private enum IntBasedEnum : int
{
Value1 = 1, Value2, Value3
}

private enum UIntBasedEnum : uint
{
Value1 = 1, Value2, Value3
}

private enum LongBasedEnum : long
{
Value1 = 1, Value2, Value3
}

private enum ULongBasedEnum : ulong
{
Value1 = 1, Value2, Value3
}


private Expression[] Expressions;

[OneTimeSetUp]
public void TestFixtureSetUp()
{
Expressions = new[] {
// non-enum constants
Expression.Constant(1, typeof(int)),
Expression.Constant(2, typeof(int?)),
Expression.Constant(null, typeof(int?)),

//short enums
Expression.Constant(ShortBasedEnum.Value1, typeof(ShortBasedEnum)),
Expression.Constant(ShortBasedEnum.Value1, typeof(ShortBasedEnum)),
Expression.Constant(ShortBasedEnum.Value1, typeof(ShortBasedEnum)),
Expression.Constant(ShortBasedEnum.Value1, typeof(ShortBasedEnum)),
Expression.Constant(IntBasedEnum.Value1, typeof(IntBasedEnum)),
Expression.Constant(LongBasedEnum.Value1, typeof(LongBasedEnum)),

Expression.Constant(ShortBasedEnum.Value2, typeof(ShortBasedEnum?)),
Expression.Constant(IntBasedEnum.Value2, typeof(IntBasedEnum?)),
Expression.Constant(LongBasedEnum.Value2, typeof(LongBasedEnum?)),

Expression.Constant(null, typeof(ShortBasedEnum?)),
Expression.Constant(null, typeof(IntBasedEnum?)),
Expression.Constant(null, typeof(LongBasedEnum?)),
};
}

[Test]
public void NonEnumValuesTest()
{
foreach (var exp in Expressions.Take(3)) {
var rewrited = EnumRewriter.Rewrite(exp);
Assert.That(rewrited, Is.EqualTo(exp));
}
}

[Test]
public void NonNullableEnumsTest()
{
foreach (var exp in Expressions.Skip(3).Take(3)) {
var rewrited = EnumRewriter.Rewrite(exp);
var expType = exp.Type;
var enumType = expType.StripNullable();
Assert.That(rewrited, Is.InstanceOf<UnaryExpression>());
var convert = rewrited as UnaryExpression;
Assert.That(convert.NodeType, Is.EqualTo(ExpressionType.Convert));
Assert.That(convert.Type, Is.EqualTo(expType));
var operand = convert.Operand;
Assert.That(operand, Is.InstanceOf<ConstantExpression>());
var constant = operand as ConstantExpression;
Assert.That(constant.Type, Is.Not.EqualTo(enumType));
Assert.That(constant.Type, Is.EqualTo(Enum.GetUnderlyingType(enumType)));
}
}

[Test]
public void NullableEnumsTest()
{
foreach (var exp in Expressions.Skip(6).Take(3)) {
var rewrited = EnumRewriter.Rewrite(exp);
var expType = exp.Type;
var enumType = expType.StripNullable();

Assert.That(rewrited, Is.InstanceOf<UnaryExpression>());
var convert = rewrited as UnaryExpression;
Assert.That(convert.NodeType, Is.EqualTo(ExpressionType.Convert));
Assert.That(convert.Type, Is.EqualTo(expType));
var operand = convert.Operand;
Assert.That(operand, Is.InstanceOf<ConstantExpression>());
var constant = operand as ConstantExpression;
Assert.That(constant.Type, Is.Not.EqualTo(enumType));
Assert.That(constant.Type, Is.EqualTo(Enum.GetUnderlyingType(enumType)));
Assert.That(constant.Value, Is.GreaterThan(1));

}
}

[Test]
public void NullsAsNullableEnumsTest()
{
foreach (var exp in Expressions.Skip(9).Take(3)) {

var rewrited = EnumRewriter.Rewrite(exp);
var expType = exp.Type;
var enumType = expType.StripNullable();

Assert.That(rewrited, Is.InstanceOf<UnaryExpression>());
var convert = rewrited as UnaryExpression;
Assert.That(convert.NodeType, Is.EqualTo(ExpressionType.Convert));
Assert.That(convert.Type, Is.EqualTo(expType));
var operand = convert.Operand;
Assert.That(operand, Is.InstanceOf<ConstantExpression>());
var constant = operand as ConstantExpression;
Assert.That(constant.Type, Is.Not.EqualTo(enumType));
Assert.That(constant.Type, Is.EqualTo(typeof(object)));
Assert.That(constant.Value, Is.Null);
}
}

//[Test]
//public void ComplexTest()
//{
// foreach (var exp in Expressions) {
// var rewrited = EnumRewriter.Rewrite(exp);
// var expType = exp.Type;
// if (exp is ConstantExpression testExp && expType.StripNullable().IsEnum) {
// var isNullable = expType.IsNullable();
// var enumType = expType.StripNullable();
// if (isNullable) {

// }
// else {
// Assert.That(rewrited, Is.InstanceOf<UnaryExpression>());
// var convert = rewrited as UnaryExpression;
// Assert.That(convert.NodeType, Is.EqualTo(ExpressionType.Convert));
// Assert.That(convert.Type, Is.EqualTo(expType));
// var operand = convert.Operand;
// Assert.That(operand, Is.InstanceOf<ConstantExpression>());
// var constant = operand as ConstantExpression;
// Assert.That(constant.Type, Is.Not.EqualTo(enumType));
// Assert.That(constant.Type, Is.EqualTo(Enum.GetUnderlyingType(enumType)));
// }
// }
// else {
// Assert.That(rewrited, Is.EqualTo(exp));
// }
// }
//}
}
}
14 changes: 14 additions & 0 deletions Orm/Xtensive.Orm.Tests/Linq/GroupByTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,20 @@ public void GroupByBoolExpressionComplex()
Assert.AreEqual(falseResult, result.Single(i => !i.Value).Count);
Assert.AreEqual(trueResult, result.Single(i => i.Value).Count);
}

[Test]
public void GroupByEnumTernaryWithNonNullConstTest()
{
var query = Session.Query.All<Invoice>()
.GroupBy(c => c.Total < 0 ? (InvoiceStatus?) InvoiceStatus.Completed : c.Status).ToArray();
}

[Test]
public void GroupByEnumTernaryWithNullConstTest()
{
var query = Session.Query.All<Invoice>()
.GroupBy(c => c.Total < 0 ? (InvoiceStatus?) null : c.Status).ToArray();
}

private void DumpGrouping<TKey, TValue>(IQueryable<IGrouping<TKey, TValue>> result)
{
Expand Down
Loading