Skip to content

Commit d055153

Browse files
authored
Merge pull request #412 from DataObjects-NET/7.1-enum-conversion-issue
Improves support for enum constants within queries
2 parents 7f1264a + ccd8c60 commit d055153

File tree

6 files changed

+394
-92
lines changed

6 files changed

+394
-92
lines changed

ChangeLog/7.1.3_dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
[main] Addressed race condition issue with TranslatorState.NonVisitableExpressions
2+
[main] Improved working with nullable enum constants in queries
23
[postgresql] Improved database structucture extraction
34
[postgresql] Addressed certain cases of decimal results of aggregate operations being unable to fit to .NET decimal
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
using System;
2+
using System.Linq.Expressions;
3+
using System.Collections.Generic;
4+
using System.Text;
5+
using Xtensive.Reflection;
6+
using Xtensive.Orm.Linq.Expressions.Visitors;
7+
using NUnit.Framework;
8+
using System.Linq;
9+
10+
namespace Xtensive.Orm.Tests.Core.Linq
11+
{
12+
public class EnumRewriterTest
13+
{
14+
private enum ByteBasedEnum : byte
15+
{
16+
Value1 = 1, Value2, Value3
17+
}
18+
19+
private enum SByteBasedEnum : sbyte
20+
{
21+
Value1 = 1, Value2, Value3
22+
}
23+
24+
private enum ShortBasedEnum : short
25+
{
26+
Value1 = 1, Value2, Value3
27+
}
28+
29+
private enum UShortBasedEnum : ushort
30+
{
31+
Value1 = 1, Value2, Value3
32+
}
33+
34+
private enum IntBasedEnum : int
35+
{
36+
Value1 = 1, Value2, Value3
37+
}
38+
39+
private enum UIntBasedEnum : uint
40+
{
41+
Value1 = 1, Value2, Value3
42+
}
43+
44+
private enum LongBasedEnum : long
45+
{
46+
Value1 = 1, Value2, Value3
47+
}
48+
49+
private enum ULongBasedEnum : ulong
50+
{
51+
Value1 = 1, Value2, Value3
52+
}
53+
54+
55+
private Expression[] Expressions;
56+
57+
[OneTimeSetUp]
58+
public void TestFixtureSetUp()
59+
{
60+
Expressions = new[] {
61+
// non-enum constants
62+
Expression.Constant(1, typeof(int)),
63+
Expression.Constant(2, typeof(int?)),
64+
Expression.Constant(null, typeof(int?)),
65+
66+
//short enums
67+
Expression.Constant(ShortBasedEnum.Value1, typeof(ShortBasedEnum)),
68+
Expression.Constant(ShortBasedEnum.Value1, typeof(ShortBasedEnum)),
69+
Expression.Constant(ShortBasedEnum.Value1, typeof(ShortBasedEnum)),
70+
Expression.Constant(ShortBasedEnum.Value1, typeof(ShortBasedEnum)),
71+
Expression.Constant(IntBasedEnum.Value1, typeof(IntBasedEnum)),
72+
Expression.Constant(LongBasedEnum.Value1, typeof(LongBasedEnum)),
73+
74+
Expression.Constant(ShortBasedEnum.Value2, typeof(ShortBasedEnum?)),
75+
Expression.Constant(IntBasedEnum.Value2, typeof(IntBasedEnum?)),
76+
Expression.Constant(LongBasedEnum.Value2, typeof(LongBasedEnum?)),
77+
78+
Expression.Constant(null, typeof(ShortBasedEnum?)),
79+
Expression.Constant(null, typeof(IntBasedEnum?)),
80+
Expression.Constant(null, typeof(LongBasedEnum?)),
81+
};
82+
}
83+
84+
[Test]
85+
public void NonEnumValuesTest()
86+
{
87+
foreach (var exp in Expressions.Take(3)) {
88+
var rewrited = EnumRewriter.Rewrite(exp);
89+
Assert.That(rewrited, Is.EqualTo(exp));
90+
}
91+
}
92+
93+
[Test]
94+
public void NonNullableEnumsTest()
95+
{
96+
foreach (var exp in Expressions.Skip(3).Take(3)) {
97+
var rewrited = EnumRewriter.Rewrite(exp);
98+
var expType = exp.Type;
99+
var enumType = expType.StripNullable();
100+
Assert.That(rewrited, Is.InstanceOf<UnaryExpression>());
101+
var convert = rewrited as UnaryExpression;
102+
Assert.That(convert.NodeType, Is.EqualTo(ExpressionType.Convert));
103+
Assert.That(convert.Type, Is.EqualTo(expType));
104+
var operand = convert.Operand;
105+
Assert.That(operand, Is.InstanceOf<ConstantExpression>());
106+
var constant = operand as ConstantExpression;
107+
Assert.That(constant.Type, Is.Not.EqualTo(enumType));
108+
Assert.That(constant.Type, Is.EqualTo(Enum.GetUnderlyingType(enumType)));
109+
}
110+
}
111+
112+
[Test]
113+
public void NullableEnumsTest()
114+
{
115+
foreach (var exp in Expressions.Skip(6).Take(3)) {
116+
var rewrited = EnumRewriter.Rewrite(exp);
117+
var expType = exp.Type;
118+
var enumType = expType.StripNullable();
119+
120+
Assert.That(rewrited, Is.InstanceOf<UnaryExpression>());
121+
var convert = rewrited as UnaryExpression;
122+
Assert.That(convert.NodeType, Is.EqualTo(ExpressionType.Convert));
123+
Assert.That(convert.Type, Is.EqualTo(expType));
124+
var operand = convert.Operand;
125+
Assert.That(operand, Is.InstanceOf<ConstantExpression>());
126+
var constant = operand as ConstantExpression;
127+
Assert.That(constant.Type, Is.Not.EqualTo(enumType));
128+
Assert.That(constant.Type, Is.EqualTo(Enum.GetUnderlyingType(enumType)));
129+
Assert.That(constant.Value, Is.GreaterThan(1));
130+
131+
}
132+
}
133+
134+
[Test]
135+
public void NullsAsNullableEnumsTest()
136+
{
137+
foreach (var exp in Expressions.Skip(9).Take(3)) {
138+
139+
var rewrited = EnumRewriter.Rewrite(exp);
140+
var expType = exp.Type;
141+
var enumType = expType.StripNullable();
142+
143+
Assert.That(rewrited, Is.InstanceOf<UnaryExpression>());
144+
var convert = rewrited as UnaryExpression;
145+
Assert.That(convert.NodeType, Is.EqualTo(ExpressionType.Convert));
146+
Assert.That(convert.Type, Is.EqualTo(expType));
147+
var operand = convert.Operand;
148+
Assert.That(operand, Is.InstanceOf<ConstantExpression>());
149+
var constant = operand as ConstantExpression;
150+
Assert.That(constant.Type, Is.Not.EqualTo(enumType));
151+
Assert.That(constant.Type, Is.EqualTo(typeof(object)));
152+
Assert.That(constant.Value, Is.Null);
153+
}
154+
}
155+
156+
//[Test]
157+
//public void ComplexTest()
158+
//{
159+
// foreach (var exp in Expressions) {
160+
// var rewrited = EnumRewriter.Rewrite(exp);
161+
// var expType = exp.Type;
162+
// if (exp is ConstantExpression testExp && expType.StripNullable().IsEnum) {
163+
// var isNullable = expType.IsNullable();
164+
// var enumType = expType.StripNullable();
165+
// if (isNullable) {
166+
167+
// }
168+
// else {
169+
// Assert.That(rewrited, Is.InstanceOf<UnaryExpression>());
170+
// var convert = rewrited as UnaryExpression;
171+
// Assert.That(convert.NodeType, Is.EqualTo(ExpressionType.Convert));
172+
// Assert.That(convert.Type, Is.EqualTo(expType));
173+
// var operand = convert.Operand;
174+
// Assert.That(operand, Is.InstanceOf<ConstantExpression>());
175+
// var constant = operand as ConstantExpression;
176+
// Assert.That(constant.Type, Is.Not.EqualTo(enumType));
177+
// Assert.That(constant.Type, Is.EqualTo(Enum.GetUnderlyingType(enumType)));
178+
// }
179+
// }
180+
// else {
181+
// Assert.That(rewrited, Is.EqualTo(exp));
182+
// }
183+
// }
184+
//}
185+
}
186+
}

Orm/Xtensive.Orm.Tests/Linq/GroupByTest.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,6 +1051,20 @@ public void GroupByBoolExpressionComplex()
10511051
Assert.AreEqual(falseResult, result.Single(i => !i.Value).Count);
10521052
Assert.AreEqual(trueResult, result.Single(i => i.Value).Count);
10531053
}
1054+
1055+
[Test]
1056+
public void GroupByEnumTernaryWithNonNullConstTest()
1057+
{
1058+
var query = Session.Query.All<Invoice>()
1059+
.GroupBy(c => c.Total < 0 ? (InvoiceStatus?) InvoiceStatus.Completed : c.Status).ToArray();
1060+
}
1061+
1062+
[Test]
1063+
public void GroupByEnumTernaryWithNullConstTest()
1064+
{
1065+
var query = Session.Query.All<Invoice>()
1066+
.GroupBy(c => c.Total < 0 ? (InvoiceStatus?) null : c.Status).ToArray();
1067+
}
10541068

10551069
private void DumpGrouping<TKey, TValue>(IQueryable<IGrouping<TKey, TValue>> result)
10561070
{

0 commit comments

Comments
 (0)