Skip to content

Commit 6987cd2

Browse files
authored
Null propagation for methods (#309)
* update message * fix * fix
1 parent fc2bdf4 commit 6987cd2

File tree

4 files changed

+51
-14
lines changed

4 files changed

+51
-14
lines changed

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

+15-5
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ public bool TryGenerateAndAlsoNotNullExpression(Expression sourceExpression, out
250250

251251
var expressions = CollectExpressions(sourceExpression);
252252

253-
if (expressions.Count == 1)
253+
if (expressions.Count == 1 && !(expressions[0] is MethodCallExpression))
254254
{
255255
generatedExpression = sourceExpression;
256256
return false;
@@ -274,14 +274,19 @@ public bool TryGenerateAndAlsoNotNullExpression(Expression sourceExpression, out
274274

275275
private static Expression GetMemberExpression(Expression expression)
276276
{
277+
if (expression is MemberExpression memberExpression)
278+
{
279+
return memberExpression;
280+
}
281+
277282
if (expression is ParameterExpression parameterExpression)
278283
{
279284
return parameterExpression;
280285
}
281286

282-
if (expression is MemberExpression memberExpression)
287+
if (expression is MethodCallExpression methodCallExpression)
283288
{
284-
return memberExpression;
289+
return methodCallExpression;
285290
}
286291

287292
if (expression is LambdaExpression lambdaExpression)
@@ -291,9 +296,9 @@ private static Expression GetMemberExpression(Expression expression)
291296
return bodyAsMemberExpression;
292297
}
293298

294-
if (lambdaExpression.Body is UnaryExpression bodyAsunaryExpression)
299+
if (lambdaExpression.Body is UnaryExpression bodyAsUnaryExpression)
295300
{
296-
return bodyAsunaryExpression.Operand;
301+
return bodyAsUnaryExpression.Operand;
297302
}
298303
}
299304

@@ -319,6 +324,11 @@ private static List<Expression> CollectExpressions(Expression sourceExpression)
319324
list.Add(expression);
320325
}
321326

327+
if (expression is MethodCallExpression)
328+
{
329+
list.Add(expression);
330+
}
331+
322332
return list;
323333
}
324334
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ Expression ParseIn()
352352

353353
if (_methodFinder.FindMethod(typeof(IEnumerableSignatures), nameof(IEnumerableSignatures.Contains), false, args, out MethodBase containsSignature) != 1)
354354
{
355-
throw ParseError(op.Pos, Res.NoApplicableAggregate, nameof(IEnumerableSignatures.Contains));
355+
throw ParseError(op.Pos, Res.NoApplicableAggregate, nameof(IEnumerableSignatures.Contains), string.Join(",", args.Select(a => a.Type.Name).ToArray()));
356356
}
357357

358358
var typeArgs = new[] { left.Type };
@@ -1738,7 +1738,7 @@ Expression ParseAggregate(Expression instance, Type elementType, string methodNa
17381738

17391739
if (!_methodFinder.ContainsMethod(typeof(IEnumerableSignatures), methodName, false, args))
17401740
{
1741-
throw ParseError(errorPos, Res.NoApplicableAggregate, methodName);
1741+
throw ParseError(errorPos, Res.NoApplicableAggregate, methodName, string.Join(",", args.Select(a => a.Type.Name).ToArray()));
17421742
}
17431743

17441744
Type callType = typeof(Enumerable);

src/System.Linq.Dynamic.Core/Res.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ internal static class Res
4444
public const string MinusCannotBeAppliedToUnsignedInteger = "'-' cannot be applied to unsigned integers.";
4545
public const string MissingAsClause = "Expression is missing an 'as' clause";
4646
public const string NeitherTypeConvertsToOther = "Neither of the types '{0}' and '{1}' converts to the other";
47-
public const string NoApplicableAggregate = "No applicable aggregate method '{0}' exists";
47+
public const string NoApplicableAggregate = "No applicable aggregate method '{0}({1})' exists";
4848
public const string NoApplicableIndexer = "No applicable indexer exists in type '{0}'";
4949
public const string NoApplicableMethod = "No applicable method '{0}' exists in type '{1}'";
5050
public const string NoItInScope = "No 'it' is in scope";

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

+33-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
using System.Collections.Generic;
1+
using Newtonsoft.Json.Linq;
2+
using NFluent;
3+
using System.Collections.Generic;
24
using System.Dynamic;
35
using System.Globalization;
46
using System.Linq.Dynamic.Core.Exceptions;
57
using System.Linq.Dynamic.Core.Tests.Helpers;
68
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
7-
using Newtonsoft.Json.Linq;
8-
using NFluent;
99
using Xunit;
1010

1111
namespace System.Linq.Dynamic.Core.Tests
@@ -1381,13 +1381,40 @@ public void ExpressionTests_NullPropagating(string test, string query)
13811381
Check.That(queryAsString).Equals(query);
13821382
}
13831383

1384+
[Fact]
1385+
public void ExpressionTests_NullPropagation_Method()
1386+
{
1387+
// Arrange
1388+
var users = new[] { new User { Roles = new List<Role>() } }.AsQueryable();
1389+
1390+
// Act
1391+
var resultDynamic = users.Select("np(Roles.FirstOrDefault(false).Name)").ToDynamicArray();
1392+
1393+
// Assert
1394+
Assert.True(resultDynamic[0] == null);
1395+
}
1396+
1397+
[Fact]
1398+
public void ExpressionTests_NullPropagation_Method_WithDefaultValue()
1399+
{
1400+
// Arrange
1401+
var defaultRoleName = "x";
1402+
var users = new[] { new User { Roles = new List<Role>() } }.AsQueryable();
1403+
1404+
// Act
1405+
var resultDynamic = users.Select("np(Roles.FirstOrDefault(false).Name, @0)", defaultRoleName).ToDynamicArray();
1406+
1407+
// Assert
1408+
Assert.True(resultDynamic[0] == "x");
1409+
}
1410+
13841411
[Fact]
13851412
public void ExpressionTests_NullPropagating_DateTime()
13861413
{
13871414
// Arrange
13881415
var q = new[]
13891416
{
1390-
new { id = 1, date1 = (DateTime?) DateTime.Now, date2 = DateTime.Now.AddDays(-1)}
1417+
new { id = 1, date1 = (DateTime?) DateTime.Now, date2 = DateTime.Now.AddDays(-1) }
13911418
}.AsQueryable();
13921419

13931420
// Act
@@ -1399,7 +1426,7 @@ public void ExpressionTests_NullPropagating_DateTime()
13991426
}
14001427

14011428
[Fact]
1402-
public void ExpressionTests_NullPropagating_NullableDateTime()
1429+
public void ExpressionTests_NullPropagation_NullableDateTime()
14031430
{
14041431
// Arrange
14051432
var q = new[]
@@ -1448,7 +1475,7 @@ public void ExpressionTests_NullPropagating_WithDefaultValue()
14481475
}
14491476

14501477
[Fact]
1451-
public void ExpressionTests_NullPropagating_ThrowsException()
1478+
public void ExpressionTests_NullPropagation_ThrowsException()
14521479
{
14531480
// Arrange
14541481
var q = User.GenerateSampleModels(1, true).AsQueryable();

0 commit comments

Comments
 (0)