Skip to content

Commit f523d2b

Browse files
Merge pull request #443 from zzzprojects/stef_np_432
Fix MethodCallExpression when using NullPropagating (np)
2 parents 873e3cb + 3105ca1 commit f523d2b

File tree

4 files changed

+142
-14
lines changed

4 files changed

+142
-14
lines changed

src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs

+15-4
Original file line numberDiff line numberDiff line change
@@ -319,13 +319,12 @@ private List<Expression> CollectExpressions(bool addSelf, Expression sourceExpre
319319
{
320320
case MemberExpression memberExpression:
321321
expression = GetMemberExpression(memberExpression.Expression);
322-
expressionRecognized = true;
322+
expressionRecognized = expression != null;
323323
break;
324324

325325
case MethodCallExpression methodCallExpression:
326-
// FIX method without parameter: https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/432
327-
expression = methodCallExpression.Arguments.FirstOrDefault();
328-
expressionRecognized = true;
326+
expression = GetMethodCallExpression(methodCallExpression);
327+
expressionRecognized = expression != null;
329328
break;
330329

331330
default:
@@ -341,5 +340,17 @@ private List<Expression> CollectExpressions(bool addSelf, Expression sourceExpre
341340

342341
return list;
343342
}
343+
344+
private static Expression GetMethodCallExpression(MethodCallExpression methodCallExpression)
345+
{
346+
if (methodCallExpression.Object != null)
347+
{
348+
// Something like: "np(FooValue.Zero().Length)"
349+
return methodCallExpression.Object;
350+
}
351+
352+
// Something like: "np(MyClasses.FirstOrDefault())"
353+
return methodCallExpression.Arguments.FirstOrDefault();
354+
}
344355
}
345356
}

test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs

+71-9
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1-
using NFluent;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using System.Linq.Dynamic.Core.CustomTypeProviders;
43
using System.Linq.Dynamic.Core.Exceptions;
54
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
5+
using System.Linq.Dynamic.Core.Tests.TestHelpers;
66
using System.Linq.Expressions;
77
using System.Reflection;
88
using System.Runtime.CompilerServices;
99
using FluentAssertions;
10+
using NFluent;
1011
using Xunit;
11-
using User = System.Linq.Dynamic.Core.Tests.Helpers.Models.User;
12-
using System.Linq.Dynamic.Core.Tests.TestHelpers;
1312

1413
namespace System.Linq.Dynamic.Core.Tests
1514
{
1615
public class DynamicExpressionParserTests
1716
{
17+
public class Foo
18+
{
19+
public Foo FooValue { get; set; }
20+
21+
public string Zero() => null;
22+
23+
public string One(int x) => null;
24+
25+
public string Two(int x, int y) => null;
26+
}
27+
1828
private class MyClass
1929
{
2030
public List<string> MyStrings { get; set; }
@@ -291,15 +301,15 @@ public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQu
291301
dynamic wrappedObj = constantExpression.Value;
292302

293303
var propertyInfo = wrappedObj.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public);
294-
int value = (int) propertyInfo.GetValue(wrappedObj);
304+
int value = (int)propertyInfo.GetValue(wrappedObj);
295305

296306
Check.That(value).IsEqualTo(42);
297307
}
298308

299309
[Theory]
300310
[InlineData("NullableIntValue", "42")]
301311
[InlineData("NullableDoubleValue", "42.23")]
302-
public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_ForNullableProperty_true(string propName, string valueString)
312+
public void DynamicExpressionParser_ParseLambda_UseParameterizedNamesInDynamicQuery_ForNullableProperty_true(string propName, string valueString)
303313
{
304314
// Assign
305315
var config = new ParsingConfig
@@ -1102,21 +1112,73 @@ public void DynamicExpressionParser_ParseLambda_SupportEnumerationStringComparis
11021112
Check.That(result).IsEqualTo(expectedResult);
11031113
}
11041114

1115+
[Fact]
1116+
public void DynamicExpressionParser_ParseLambda_NullPropagation_InstanceMethod_Zero_Arguments()
1117+
{
1118+
// Arrange
1119+
var expression = "np(FooValue.Zero().Length)";
1120+
1121+
// Act
1122+
var lambdaExpression = DynamicExpressionParser.ParseLambda(typeof(Foo), null, expression, new Foo());
1123+
1124+
// Assert
1125+
#if NET452
1126+
lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.Zero() != null)), Convert(Param_0.FooValue.Zero().Length), null)");
1127+
#else
1128+
lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.Zero() != null)), Convert(Param_0.FooValue.Zero().Length, Nullable`1), null)");
1129+
#endif
1130+
}
1131+
1132+
[Fact]
1133+
public void DynamicExpressionParser_ParseLambda_NullPropagation_InstanceMethod_One_Argument()
1134+
{
1135+
// Arrange
1136+
var expression = "np(FooValue.One(1).Length)";
1137+
1138+
// Act
1139+
var lambdaExpression = DynamicExpressionParser.ParseLambda(typeof(Foo), null, expression, new Foo());
1140+
1141+
// Assert
1142+
#if NET452
1143+
lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.One(1) != null)), Convert(Param_0.FooValue.One(1).Length), null)");
1144+
#else
1145+
lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.One(1) != null)), Convert(Param_0.FooValue.One(1).Length, Nullable`1), null)");
1146+
#endif
1147+
}
1148+
1149+
[Fact]
1150+
public void DynamicExpressionParser_ParseLambda_NullPropagation_InstanceMethod_Two_Arguments()
1151+
{
1152+
// Arrange
1153+
var expression = "np(FooValue.Two(1, 42).Length)";
1154+
1155+
// Act
1156+
var lambdaExpression = DynamicExpressionParser.ParseLambda(typeof(Foo), null, expression, new Foo());
1157+
1158+
// Assert
1159+
#if NET452
1160+
lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.Two(1, 42) != null)), Convert(Param_0.FooValue.Two(1, 42).Length), null)");
1161+
#else
1162+
lambdaExpression.ToString().Should().Be("Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.FooValue != null)) AndAlso (Param_0.FooValue.Two(1, 42) != null)), Convert(Param_0.FooValue.Two(1, 42).Length, Nullable`1), null)");
1163+
#endif
1164+
}
1165+
11051166
[Fact]
11061167
public void DynamicExpressionParser_ParseLambda_NullPropagation_MethodCallExpression()
11071168
{
11081169
// Arrange
1109-
var dataSource = new MyClass();
1170+
var myClass = new MyClass();
1171+
var dataSource = new { MyClasses = new[] { myClass, null } };
11101172

11111173
var expressionText = "np(MyClasses.FirstOrDefault())";
11121174

11131175
// Act
11141176
LambdaExpression expression = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, dataSource.GetType(), typeof(MyClass), expressionText);
11151177
Delegate del = expression.Compile();
1116-
MyClass result = del.DynamicInvoke(dataSource) as MyClass;
1178+
var result = del.DynamicInvoke(dataSource) as MyClass;
11171179

11181180
// Assert
1119-
result.Should().BeNull();
1181+
result.Should().Be(myClass);
11201182
}
11211183

11221184
[Theory]

test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs

+55-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Newtonsoft.Json.Linq;
1+
using FluentAssertions;
2+
using Newtonsoft.Json.Linq;
23
using NFluent;
34
using System.Collections.Generic;
45
using System.Dynamic;
@@ -49,6 +50,17 @@ public class TestObjectIdClass
4950
public long ObjectId { get; set; }
5051
}
5152

53+
public class Foo
54+
{
55+
public Foo FooValue { get; set; }
56+
57+
public string Zero() => null;
58+
59+
public string One(int x) => null;
60+
61+
public string Two(int x, int y) => null;
62+
}
63+
5264
[Fact]
5365
public void ExpressionTests_Add_Number()
5466
{
@@ -1394,6 +1406,48 @@ public void ExpressionTests_NullPropagating(string test, string query)
13941406
Check.That(queryAsString).Equals(query);
13951407
}
13961408

1409+
[Fact]
1410+
public void ExpressionTests_NullPropagating_InstanceMethod_Zero_Arguments()
1411+
{
1412+
// Arrange 1
1413+
var expression = "np(FooValue.Zero().Length)";
1414+
var q = new[] { new Foo { FooValue = new Foo() } }.AsQueryable();
1415+
1416+
// Act 2
1417+
var result = q.Select(expression).FirstOrDefault() as int?;
1418+
1419+
// Assert 2
1420+
result.Should().BeNull();
1421+
}
1422+
1423+
[Fact]
1424+
public void ExpressionTests_NullPropagating_InstanceMethod_One_Argument()
1425+
{
1426+
// Arrange
1427+
var expression = "np(FooValue.One(1).Length)";
1428+
var q = new[] { new Foo { FooValue = new Foo() } }.AsQueryable();
1429+
1430+
// Act
1431+
var result = q.Select(expression).FirstOrDefault() as int?;
1432+
1433+
// Assert
1434+
result.Should().BeNull();
1435+
}
1436+
1437+
[Fact]
1438+
public void ExpressionTests_NullPropagating_InstanceMethod_Two_Arguments()
1439+
{
1440+
// Arrange
1441+
var expression = "np(FooValue.Two(1, 42).Length)";
1442+
var q = new[] { new Foo { FooValue = new Foo() } }.AsQueryable();
1443+
1444+
// Act
1445+
var result = q.Select(expression).FirstOrDefault() as int?;
1446+
1447+
// Assert
1448+
result.Should().BeNull();
1449+
}
1450+
13971451
[Fact]
13981452
public void ExpressionTests_NullPropagation_Method()
13991453
{

test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<PackageReference Include="xunit" Version="2.4.1" />
3838
<PackageReference Include="NFluent" Version="2.7.0" />
3939
<PackageReference Include="Moq" Version="4.13.1" />
40+
<!--<PackageReference Include="ExpressionTreeToString" Version="3.1.47" />-->
4041
</ItemGroup>
4142

4243
<ItemGroup Condition=" '$(TargetFramework)' == 'net452' ">

0 commit comments

Comments
 (0)