Skip to content
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

Null propagation for methods #309

Merged
merged 5 commits into from
Nov 27, 2019
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
20 changes: 15 additions & 5 deletions src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ public bool TryGenerateAndAlsoNotNullExpression(Expression sourceExpression, out

var expressions = CollectExpressions(sourceExpression);

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

private static Expression GetMemberExpression(Expression expression)
{
if (expression is MemberExpression memberExpression)
{
return memberExpression;
}

if (expression is ParameterExpression parameterExpression)
{
return parameterExpression;
}

if (expression is MemberExpression memberExpression)
if (expression is MethodCallExpression methodCallExpression)
{
return memberExpression;
return methodCallExpression;
}

if (expression is LambdaExpression lambdaExpression)
Expand All @@ -291,9 +296,9 @@ private static Expression GetMemberExpression(Expression expression)
return bodyAsMemberExpression;
}

if (lambdaExpression.Body is UnaryExpression bodyAsunaryExpression)
if (lambdaExpression.Body is UnaryExpression bodyAsUnaryExpression)
{
return bodyAsunaryExpression.Operand;
return bodyAsUnaryExpression.Operand;
}
}

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

if (expression is MethodCallExpression)
{
list.Add(expression);
}

return list;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ Expression ParseIn()

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

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

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

Type callType = typeof(Enumerable);
Expand Down
2 changes: 1 addition & 1 deletion src/System.Linq.Dynamic.Core/Res.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ internal static class Res
public const string MinusCannotBeAppliedToUnsignedInteger = "'-' cannot be applied to unsigned integers.";
public const string MissingAsClause = "Expression is missing an 'as' clause";
public const string NeitherTypeConvertsToOther = "Neither of the types '{0}' and '{1}' converts to the other";
public const string NoApplicableAggregate = "No applicable aggregate method '{0}' exists";
public const string NoApplicableAggregate = "No applicable aggregate method '{0}({1})' exists";
public const string NoApplicableIndexer = "No applicable indexer exists in type '{0}'";
public const string NoApplicableMethod = "No applicable method '{0}' exists in type '{1}'";
public const string NoItInScope = "No 'it' is in scope";
Expand Down
39 changes: 33 additions & 6 deletions test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using NFluent;
using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.Linq.Dynamic.Core.Exceptions;
using System.Linq.Dynamic.Core.Tests.Helpers;
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
using Newtonsoft.Json.Linq;
using NFluent;
using Xunit;

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

[Fact]
public void ExpressionTests_NullPropagation_Method()
{
// Arrange
var users = new[] { new User { Roles = new List<Role>() } }.AsQueryable();

// Act
var resultDynamic = users.Select("np(Roles.FirstOrDefault(false).Name)").ToDynamicArray();

// Assert
Assert.True(resultDynamic[0] == null);
}

[Fact]
public void ExpressionTests_NullPropagation_Method_WithDefaultValue()
{
// Arrange
var defaultRoleName = "x";
var users = new[] { new User { Roles = new List<Role>() } }.AsQueryable();

// Act
var resultDynamic = users.Select("np(Roles.FirstOrDefault(false).Name, @0)", defaultRoleName).ToDynamicArray();

// Assert
Assert.True(resultDynamic[0] == "x");
}

[Fact]
public void ExpressionTests_NullPropagating_DateTime()
{
// Arrange
var q = new[]
{
new { id = 1, date1 = (DateTime?) DateTime.Now, date2 = DateTime.Now.AddDays(-1)}
new { id = 1, date1 = (DateTime?) DateTime.Now, date2 = DateTime.Now.AddDays(-1) }
}.AsQueryable();

// Act
Expand All @@ -1399,7 +1426,7 @@ public void ExpressionTests_NullPropagating_DateTime()
}

[Fact]
public void ExpressionTests_NullPropagating_NullableDateTime()
public void ExpressionTests_NullPropagation_NullableDateTime()
{
// Arrange
var q = new[]
Expand Down Expand Up @@ -1448,7 +1475,7 @@ public void ExpressionTests_NullPropagating_WithDefaultValue()
}

[Fact]
public void ExpressionTests_NullPropagating_ThrowsException()
public void ExpressionTests_NullPropagation_ThrowsException()
{
// Arrange
var q = User.GenerateSampleModels(1, true).AsQueryable();
Expand Down