Skip to content

Commit 043df3c

Browse files
author
Gokhan Abatay
committed
convert expression avoid as much as cast if its possible
test case added
1 parent f1586a9 commit 043df3c

File tree

3 files changed

+259
-21
lines changed

3 files changed

+259
-21
lines changed
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
using System;
2+
using System.Linq.Expressions;
3+
using System.Reflection;
4+
using NHibernate.Impl;
5+
using NUnit.Framework;
6+
using Expression = System.Linq.Expressions.Expression;
7+
8+
namespace NHibernate.Test.Criteria.Lambda
9+
{
10+
public static class DateTimeExtensions
11+
{
12+
public static long ToLongDateTime(this DateTime date)
13+
{
14+
return Convert.ToInt64(date.ToString("yyyyMMddHHmmss"));
15+
}
16+
}
17+
18+
[TestFixture]
19+
public class ExpressionProcessorFindValueFixture
20+
{
21+
private static object GetValue<T>(Expression<Func<T>> expression)
22+
{
23+
try
24+
{
25+
return ExpressionProcessor.FindValue(expression.Body);
26+
}
27+
catch (TargetInvocationException e)
28+
{
29+
throw e.InnerException;
30+
}
31+
}
32+
33+
private static int GetIntegerDate()
34+
{
35+
return Convert.ToInt32(DateTime.Now.ToString("yyyyMMdd"));
36+
}
37+
38+
[Test]
39+
public void GivenStaticPropertyInstanceMethodCall()
40+
{
41+
var actual = GetValue(() => DateTime.Now.ToString("yyyyMMdd"));
42+
var expected = DateTime.Now.ToString("yyyyMMdd");
43+
44+
Assert.AreEqual(expected, actual);
45+
}
46+
47+
[Test]
48+
public void GivenStaticPropertyInstanceMultipleMethodCall()
49+
{
50+
var actual = GetValue(() => DateTime.Now.AddDays(365).ToString("yyyyMMdd"));
51+
var expected = DateTime.Now.AddDays(365).ToString("yyyyMMdd");
52+
53+
Assert.AreEqual(expected, actual);
54+
}
55+
56+
[Test]
57+
public void GivenStaticPropertyInstanceMethodCallThenCast()
58+
{
59+
var actual = GetValue(() => Convert.ToInt32(DateTime.Now.AddDays(365).ToString("yyyyMMdd")));
60+
var expected = Convert.ToInt32(DateTime.Now.AddDays(365).ToString("yyyyMMdd"));
61+
62+
Assert.AreEqual(expected, actual);
63+
}
64+
65+
[Test]
66+
public void GivenStaticMethodCall()
67+
{
68+
var actual = GetValue(() => GetIntegerDate());
69+
var expected = GetIntegerDate();
70+
71+
Assert.AreEqual(expected, actual);
72+
}
73+
74+
[Test]
75+
public void GivenExtensionMethodCall()
76+
{
77+
var date = DateTime.Now;
78+
var actual = GetValue(() => date.ToLongDateTime());
79+
var expected = date.ToLongDateTime();
80+
81+
Assert.AreEqual(expected, actual);
82+
}
83+
84+
[Test]
85+
public void GivenNestedPropertyAccess()
86+
{
87+
var animal = new { Snake = new { Animal = new { Name = "Scorpion" } } };
88+
var actual = GetValue(() => animal.Snake.Animal.Name);
89+
var expected = animal.Snake.Animal.Name;
90+
91+
Assert.AreEqual(expected, actual);
92+
}
93+
94+
[Test]
95+
public void GivenGuidToStringCast()
96+
{
97+
var guid = Guid.NewGuid();
98+
Expression<Func<string>> expression = () => $"{guid}";
99+
var actual = GetValue(expression);
100+
101+
//Check with expression compile and invoke
102+
var lambdaExpression = Expression.Lambda(expression).Compile();
103+
var expected = ((dynamic) lambdaExpression.DynamicInvoke()).Invoke();
104+
105+
Assert.AreEqual(expected, actual);
106+
}
107+
108+
[Test]
109+
public void GivenNestedPropertyToIntegerCast()
110+
{
111+
var animal = new { Snake = new { Animal = new { Weight = 9.89 } } };
112+
Expression<Func<int>> expression = () => (int) animal.Snake.Animal.Weight;
113+
var actual = GetValue(expression);
114+
115+
//Check with expression compile and invoke
116+
var lambdaExpression = Expression.Lambda(expression).Compile();
117+
var expected = ((dynamic) lambdaExpression.DynamicInvoke()).Invoke();
118+
119+
Assert.AreEqual(expected, actual);
120+
}
121+
122+
[Test]
123+
public void GivenNestedPropertyToIntegerConvert()
124+
{
125+
var animal = new { Snake = new { Animal = new { Weight = 9.89 } } };
126+
Expression<Func<int>> expression = () => Convert.ToInt32(animal.Snake.Animal.Weight);
127+
var actual = GetValue(expression);
128+
129+
//Check with expression compile and invoke
130+
var lambdaExpression = Expression.Lambda(expression).Compile();
131+
var expected = ((dynamic) lambdaExpression.DynamicInvoke()).Invoke();
132+
133+
Assert.AreEqual(expected, actual);
134+
}
135+
136+
[Test]
137+
public void GivenNullToIntegerCastFails()
138+
{
139+
object value = null;
140+
Expression<Func<int>> expression = () => (int) value;
141+
142+
Assert.Throws<NullReferenceException>(() => GetValue(expression));
143+
144+
//Check with expression compile and invoke
145+
var lambdaExpression = Expression.Lambda(expression).Compile();
146+
147+
Assert.Throws<NullReferenceException>(() => ((dynamic) lambdaExpression.DynamicInvoke()).Invoke());
148+
}
149+
150+
[Test]
151+
public void GivenNullableIntegerToIntegerCastFails()
152+
{
153+
int? value = null;
154+
Expression<Func<int>> expression = () => (int) value;
155+
156+
Assert.Throws<InvalidOperationException>(() => GetValue(expression));
157+
158+
//Check with expression compile and invoke
159+
var lambdaExpression = Expression.Lambda(expression).Compile();
160+
161+
Assert.Throws<InvalidOperationException>(() => ((dynamic) lambdaExpression.DynamicInvoke()).Invoke());
162+
163+
}
164+
165+
[Test]
166+
public void GivenIntegerToNullableIntegerCast()
167+
{
168+
int value = 12345;
169+
Expression<Func<int?>> expression = () => value;
170+
171+
var actual = GetValue(expression);
172+
173+
//Check with expression compile and invoke
174+
var lambdaExpression = Expression.Lambda(expression).Compile();
175+
176+
var expected = ((dynamic) lambdaExpression.DynamicInvoke()).Invoke();
177+
178+
Assert.AreEqual(expected, actual);
179+
180+
}
181+
182+
[Test]
183+
public void GivenInt16ToIntegerCast()
184+
{
185+
short value = 12345;
186+
Expression<Func<int>> expression = () => value;
187+
188+
var actual = GetValue(expression);
189+
190+
//Check with expression compile and invoke
191+
var lambdaExpression = Expression.Lambda(expression).Compile();
192+
193+
var expected = ((dynamic) lambdaExpression.DynamicInvoke()).Invoke();
194+
195+
Assert.AreEqual(expected, actual);
196+
197+
}
198+
199+
[Test]
200+
public void GivenNullableDecimalToDecimalCast()
201+
{
202+
decimal? value = 9.89m;
203+
Expression<Func<decimal>> expression = () => (decimal) value;
204+
205+
var actual = GetValue(expression);
206+
207+
//Check with expression compile and invoke
208+
var lambdaExpression = Expression.Lambda(expression).Compile();
209+
var expected = ((dynamic) lambdaExpression.DynamicInvoke()).Invoke();
210+
211+
Assert.AreEqual(expected, actual);
212+
213+
}
214+
215+
[Test]
216+
public void GivenStringToIntegerCastFails()
217+
{
218+
object value = "Abatay";
219+
Expression<Func<int>> expression = () => (int) value;
220+
221+
Assert.Throws<InvalidCastException>(() => GetValue(expression));
222+
223+
//Check with expression compile and invoke
224+
var lambdaExpression = Expression.Lambda(expression).Compile();
225+
226+
Assert.Throws<InvalidCastException>(() => ((dynamic) lambdaExpression.DynamicInvoke()).Invoke());
227+
}
228+
229+
[Test]
230+
public void GivenBooleanToCharCastFails()
231+
{
232+
object isTrue = true;
233+
Expression<Func<char>> expression = () => (char) isTrue;
234+
235+
Assert.Throws<InvalidCastException>(() => GetValue(expression));
236+
237+
//Check with expression compile and invoke
238+
var lambdaExpression = Expression.Lambda(expression).Compile();
239+
240+
Assert.Throws<InvalidCastException>(() => ((dynamic) lambdaExpression.DynamicInvoke()).Invoke());
241+
}
242+
}
243+
}

src/NHibernate/Impl/ExpressionProcessor.cs

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -278,14 +278,7 @@ public static object FindValue(Expression expression)
278278

279279
if (methodCallExpression.Object == null) //extension or static method
280280
{
281-
if (args.Length > 0 && args[0] != null)
282-
{
283-
return methodCallExpression.Method.Invoke(args[0].GetType(), args);
284-
}
285-
else
286-
{
287-
return methodCallExpression.Method.Invoke(methodCallExpression.Method.DeclaringType, args);
288-
}
281+
return methodCallExpression.Method.Invoke(null, args);
289282
}
290283
else
291284
{
@@ -295,26 +288,28 @@ public static object FindValue(Expression expression)
295288
}
296289
case ExpressionType.Convert:
297290
var unaryExpression = (UnaryExpression) expression;
298-
value = FindValue(unaryExpression.Operand);
299-
var type = Nullable.GetUnderlyingType(unaryExpression.Type) ?? unaryExpression.Type;
300-
if (type == typeof(object) || value == null)
291+
if ((Nullable.GetUnderlyingType(unaryExpression.Type) ?? unaryExpression.Type) == unaryExpression.Operand.Type
292+
|| unaryExpression.Type == typeof(object))
301293
{
302-
return value;
294+
return FindValue(unaryExpression.Operand);
303295
}
304-
else if (value is IConvertible || unaryExpression.Method != null)
296+
else if (unaryExpression.Method != null && unaryExpression.Type != unaryExpression.Operand.Type)
305297
{
306-
if (type != unaryExpression.Operand.Type)
298+
return unaryExpression.Method.Invoke(null, new[] { FindValue(unaryExpression.Operand) });
299+
}
300+
else if (unaryExpression.Type == (Nullable.GetUnderlyingType(unaryExpression.Operand.Type) ?? unaryExpression.Operand.Type))
301+
{
302+
value = FindValue(unaryExpression.Operand);
303+
if (value != null || Nullable.GetUnderlyingType(unaryExpression.Type) != null)
307304
{
308-
value = unaryExpression.Method != null ? unaryExpression.Method.Invoke(null, new[] { value }) : Convert.ChangeType(value, type);
305+
return value;
309306
}
310-
311-
return value;
312307
}
313308
break;
314309
}
315310

316-
var valueExpression = Expression.Lambda(expression).Compile(true);
317-
value = valueExpression.DynamicInvoke();
311+
var lambdaExpression = Expression.Lambda(expression).Compile(true);
312+
value = lambdaExpression.DynamicInvoke();
318313
return value;
319314
}
320315

src/NHibernate/Linq/Visitors/ExpressionParameterVisitor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ protected override Expression VisitUnary(UnaryExpression node)
151151
node.Method != null && // The implicit/explicit operator method
152152
node.Operand is ConstantExpression constantExpression)
153153
{
154-
// Instead of getting constantExpression.Value, we override the value with ExpressionProcessor
155-
var value = ExpressionProcessor.FindValue(node);
154+
// Instead of getting constantExpression.Value, invoke method
155+
var value = node.Method.Invoke(null, null);
156156

157157
AddConstantExpressionParameter(constantExpression, value);
158158
}

0 commit comments

Comments
 (0)