Skip to content

Commit c9e98c9

Browse files
authored
Fix np(...) (#338)
* Fix np(...) for single expression * Any * fix * fix2
1 parent ffa8f68 commit c9e98c9

File tree

7 files changed

+106
-39
lines changed

7 files changed

+106
-39
lines changed

src-console/ConsoleAppEF2.0/Database/Car.cs

+4
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@ public class Car
2121
[Required]
2222
public string Color { get; set; }
2323

24+
public string Extra { get; set; }
25+
2426
[Required]
2527
public DateTime DateLastModified { get; set; }
2628

2729
public DateTime? DateDeleted { get; set; }
2830

31+
public int? NullableInt { get; set; }
32+
2933
public string X(bool b, string s)
3034
{
3135
return b + s + Color;

src-console/ConsoleAppEF3.1/ConsoleApp_netcore3.1_EF3.1.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
<ItemGroup>
2727
<ProjectReference Include="..\..\src\Microsoft.EntityFrameworkCore.DynamicLinq\Microsoft.EntityFrameworkCore.DynamicLinq.csproj" />
28+
<ProjectReference Include="..\..\src\System.Linq.Dynamic.Core\System.Linq.Dynamic.Core.csproj" />
2829
</ItemGroup>
2930

3031
</Project>

src-console/ConsoleAppEF3.1/Program.cs

+22-3
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,20 @@ static void Main(string[] args)
1919
var dateLastModified = new DateTime(2018, 1, 15);
2020
if (!context.Cars.Any())
2121
{
22-
context.Cars.Add(new Car { Brand = "Ford", Color = "Blue", Vin = "yes", Year = "2017", DateLastModified = dateLastModified, DateDeleted = dateDeleted });
22+
context.Cars.Add(new Car { Brand = "Ford", Color = "Blue", Vin = "yes", Year = "2017", Extra = "e1", NullableInt = 1, DateLastModified = dateLastModified, DateDeleted = dateDeleted });
2323
context.Cars.Add(new Car { Brand = "Fiat", Color = "Red", Vin = "yes", Year = "2016", DateLastModified = dateLastModified.AddDays(1) });
24-
context.Cars.Add(new Car { Brand = "Alfa", Color = "Black", Vin = "no", Year = "1979", DateLastModified = dateLastModified.AddDays(2) });
25-
context.Cars.Add(new Car { Brand = "Alfa", Color = "Black", Vin = "a%bc", Year = "1979", DateLastModified = dateLastModified.AddDays(3), DateDeleted = dateDeleted.AddDays(1) }); ;
24+
context.Cars.Add(new Car { Brand = "Alfa", Color = "Black", Vin = "no", Year = "1979", Extra = "e2", NullableInt = 2, DateLastModified = dateLastModified.AddDays(2) });
25+
context.Cars.Add(new Car { Brand = "Alfa", Color = "Black", Vin = "a%bc", Year = "1979", Extra = "e3", NullableInt = 3, DateLastModified = dateLastModified.AddDays(3), DateDeleted = dateDeleted.AddDays(1) }); ;
26+
context.Cars.Add(new Car { Brand = "Ford", Color = "Yellow", Vin = "no", Year = "2020", DateLastModified = dateLastModified });
2627
context.SaveChanges();
2728
}
2829

30+
var npExtra1 = context.Cars.Select("np(Extra, \"no-extra\")").ToDynamicList();
31+
var npExtra2 = context.Cars.Select("np(Extra, string.Empty)").ToDynamicList();
32+
var npExtra3 = context.Cars.Any("np(Extra, string.Empty).ToUpper() == \"e1\"");
33+
34+
var npNullableInt = context.Cars.Select("np(NullableInt, 42)").ToDynamicList();
35+
2936
var selectNullableDateTime = context.Cars.FirstOrDefault(c => c.DateDeleted == dateDeleted);
3037
Console.WriteLine($"selectNullableDateTime.Key = {selectNullableDateTime.Key}");
3138

@@ -49,6 +56,18 @@ static void Main(string[] args)
4956
{
5057
Console.WriteLine($"orderByNullableDateTimeDynamicResult2.Color,DateDeleted = {x.Color},{x.DateDeleted}");
5158
}
59+
60+
var users = new[] { new User { FirstName = "Doe" } }.AsQueryable();
61+
62+
var resultDynamic = users.Any("c => np(c.FirstName, string.Empty).ToUpper() == \"DOE\"");
63+
Console.WriteLine(resultDynamic);
64+
}
65+
66+
public class User
67+
{
68+
public string FirstName { get; set; }
69+
public string LastName { get; set; }
70+
public string EmailAddress { get; set; }
5271
}
5372
}
5473
}

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

+45-31
Original file line numberDiff line numberDiff line change
@@ -1135,15 +1135,29 @@ Expression ParseFunctionNullPropagation()
11351135

11361136
if (args[0] is MemberExpression memberExpression)
11371137
{
1138+
bool hasDefaultParameter = args.Length == 2;
1139+
Expression expressionIfFalse = hasDefaultParameter ? args[1] : Expression.Constant(null);
1140+
11381141
if (_expressionHelper.TryGenerateAndAlsoNotNullExpression(memberExpression, out Expression generatedExpression))
11391142
{
1140-
var expressionIfTrue = memberExpression;
1141-
var expressionIfFalse = args.Length == 2 ? args[1] : Expression.Constant(null);
1143+
return GenerateConditional(generatedExpression, memberExpression, expressionIfFalse, errorPos);
1144+
}
1145+
1146+
if (!hasDefaultParameter)
1147+
{
1148+
// If no default parameter has been supplied and the member expression is a single expression, just return it.
1149+
return memberExpression;
1150+
}
11421151

1143-
return GenerateConditional(generatedExpression, expressionIfTrue, expressionIfFalse, errorPos);
1152+
bool canBeNullableType = TypeHelper.IsNullableType(memberExpression.Type) || !memberExpression.Type.GetTypeInfo().IsPrimitive;
1153+
if (canBeNullableType)
1154+
{
1155+
// For nullable objects, generate 'x != null ? x : null'
1156+
var notEqualToNull = _expressionHelper.GenerateNotEqual(memberExpression, Expression.Constant(null));
1157+
return GenerateConditional(notEqualToNull, memberExpression, expressionIfFalse, errorPos);
11441158
}
11451159

1146-
// The member expression is a single expression, just return it.
1160+
// For non-nullable with default, just ignore default and return the single expression.
11471161
return memberExpression;
11481162
}
11491163

@@ -1207,59 +1221,59 @@ Expression ParseFunctionCast()
12071221
return Expression.ConvertChecked(_it, resolvedType);
12081222
}
12091223

1210-
Expression GenerateConditional(Expression test, Expression expr1, Expression expr2, int errorPos)
1224+
Expression GenerateConditional(Expression test, Expression expressionIfTrue, Expression expressionIfFalse, int errorPos)
12111225
{
12121226
if (test.Type != typeof(bool))
12131227
{
12141228
throw ParseError(errorPos, Res.FirstExprMustBeBool);
12151229
}
12161230

1217-
if (expr1.Type != expr2.Type)
1231+
if (expressionIfTrue.Type != expressionIfFalse.Type)
12181232
{
1219-
// If expr1 is a null constant and expr2 is ValueType:
1220-
// - create nullable constant from expr1 with type from expr2
1221-
// - convert expr2 to nullable (unless it's already nullable)
1222-
if (Constants.IsNull(expr1) && expr2.Type.GetTypeInfo().IsValueType)
1233+
// If expressionIfTrue is a null constant and expressionIfFalse is ValueType:
1234+
// - create nullable constant from expressionIfTrue with type from expressionIfFalse
1235+
// - convert expressionIfFalse to nullable (unless it's already nullable)
1236+
if (Constants.IsNull(expressionIfTrue) && expressionIfFalse.Type.GetTypeInfo().IsValueType)
12231237
{
1224-
Type nullableType = TypeHelper.ToNullableType(expr2.Type);
1225-
expr1 = Expression.Constant(null, nullableType);
1226-
if (!TypeHelper.IsNullableType(expr2.Type))
1238+
Type nullableType = TypeHelper.ToNullableType(expressionIfFalse.Type);
1239+
expressionIfTrue = Expression.Constant(null, nullableType);
1240+
if (!TypeHelper.IsNullableType(expressionIfFalse.Type))
12271241
{
1228-
expr2 = Expression.Convert(expr2, nullableType);
1242+
expressionIfFalse = Expression.Convert(expressionIfFalse, nullableType);
12291243
}
12301244

1231-
return Expression.Condition(test, expr1, expr2);
1245+
return Expression.Condition(test, expressionIfTrue, expressionIfFalse);
12321246
}
12331247

1234-
// If expr2 is a null constant and expr1 is a ValueType:
1235-
// - create nullable constant from expr2 with type from expr1
1236-
// - convert expr1 to nullable (unless it's already nullable)
1237-
if (Constants.IsNull(expr2) && expr1.Type.GetTypeInfo().IsValueType)
1248+
// If expressionIfFalse is a null constant and expressionIfTrue is a ValueType:
1249+
// - create nullable constant from expressionIfFalse with type from expressionIfTrue
1250+
// - convert expressionIfTrue to nullable (unless it's already nullable)
1251+
if (Constants.IsNull(expressionIfFalse) && expressionIfTrue.Type.GetTypeInfo().IsValueType)
12381252
{
1239-
Type nullableType = TypeHelper.ToNullableType(expr1.Type);
1240-
expr2 = Expression.Constant(null, nullableType);
1241-
if (!TypeHelper.IsNullableType(expr1.Type))
1253+
Type nullableType = TypeHelper.ToNullableType(expressionIfTrue.Type);
1254+
expressionIfFalse = Expression.Constant(null, nullableType);
1255+
if (!TypeHelper.IsNullableType(expressionIfTrue.Type))
12421256
{
1243-
expr1 = Expression.Convert(expr1, nullableType);
1257+
expressionIfTrue = Expression.Convert(expressionIfTrue, nullableType);
12441258
}
12451259

1246-
return Expression.Condition(test, expr1, expr2);
1260+
return Expression.Condition(test, expressionIfTrue, expressionIfFalse);
12471261
}
12481262

1249-
Expression expr1As2 = !Constants.IsNull(expr2) ? _parsingConfig.ExpressionPromoter.Promote(expr1, expr2.Type, true, false) : null;
1250-
Expression expr2As1 = !Constants.IsNull(expr1) ? _parsingConfig.ExpressionPromoter.Promote(expr2, expr1.Type, true, false) : null;
1263+
Expression expr1As2 = !Constants.IsNull(expressionIfFalse) ? _parsingConfig.ExpressionPromoter.Promote(expressionIfTrue, expressionIfFalse.Type, true, false) : null;
1264+
Expression expr2As1 = !Constants.IsNull(expressionIfTrue) ? _parsingConfig.ExpressionPromoter.Promote(expressionIfFalse, expressionIfTrue.Type, true, false) : null;
12511265
if (expr1As2 != null && expr2As1 == null)
12521266
{
1253-
expr1 = expr1As2;
1267+
expressionIfTrue = expr1As2;
12541268
}
12551269
else if (expr2As1 != null && expr1As2 == null)
12561270
{
1257-
expr2 = expr2As1;
1271+
expressionIfFalse = expr2As1;
12581272
}
12591273
else
12601274
{
1261-
string type1 = !Constants.IsNull(expr1) ? expr1.Type.Name : "null";
1262-
string type2 = !Constants.IsNull(expr2) ? expr2.Type.Name : "null";
1275+
string type1 = !Constants.IsNull(expressionIfTrue) ? expressionIfTrue.Type.Name : "null";
1276+
string type2 = !Constants.IsNull(expressionIfFalse) ? expressionIfFalse.Type.Name : "null";
12631277
if (expr1As2 != null)
12641278
{
12651279
throw ParseError(errorPos, Res.BothTypesConvertToOther, type1, type2);
@@ -1269,7 +1283,7 @@ Expression GenerateConditional(Expression test, Expression expr1, Expression exp
12691283
}
12701284
}
12711285

1272-
return Expression.Condition(test, expr1, expr2);
1286+
return Expression.Condition(test, expressionIfTrue, expressionIfFalse);
12731287
}
12741288

12751289
// new (...) function

src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
<Description>This is a .NETStandard / .NET Core port of the the Microsoft assembly for the .Net 4.0 Dynamic language functionality.</Description>
55
<AssemblyTitle>System.Linq.Dynamic.Core</AssemblyTitle>
66
<Authors>Microsoft;Scott Guthrie;King Wilder;Nathan Arnott;Stef Heyenrath</Authors>
7-
<TargetFrameworks>net35;net40;net45;net46;netstandard1.3;netstandard2.0;uap10.0;netcoreapp2.1</TargetFrameworks>
7+
<TargetFrameworks>net35;net40;net45;net46;netstandard1.3;netstandard2.0;netcoreapp2.1</TargetFrameworks>
8+
<!--<TargetFrameworks>net35;net40;net45;net46;netstandard1.3;netstandard2.0;uap10.0;netcoreapp2.1</TargetFrameworks>-->
89
<GenerateDocumentationFile>true</GenerateDocumentationFile>
910
<AssemblyName>System.Linq.Dynamic.Core</AssemblyName>
1011
<AssemblyOriginatorKeyFile>System.Linq.Dynamic.Core.snk</AssemblyOriginatorKeyFile>

test/System.Linq.Dynamic.Core.Tests/EntitiesTests.LongCountAsync.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public async Task Entities_LongCountAsync_Predicate_Args()
3535
#endif
3636

3737
//Act
38-
long result = await (_context.Blogs as IQueryable).LongCountAsync("Name.Contains(@0)", search);
38+
long result = (_context.Blogs as IQueryable).LongCount("Name.Contains(@0)", search);
3939

4040
//Assert
4141
Assert.Equal(expected, result);

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

+31-3
Original file line numberDiff line numberDiff line change
@@ -1419,7 +1419,7 @@ public void ExpressionTests_NullPropagating_DateTime()
14191419

14201420
// Act
14211421
var result = q.OrderBy(x => x.date2).Select(x => x.id).ToArray();
1422-
var resultDynamic = q.OrderBy("np(date2)").Select("id").ToDynamicArray<int>();
1422+
var resultDynamic = q.OrderBy("np(date1)").Select("id").ToDynamicArray<int>();
14231423

14241424
// Assert
14251425
Check.That(resultDynamic).ContainsExactly(result);
@@ -1443,7 +1443,7 @@ public void ExpressionTests_NullPropagation_NullableDateTime()
14431443
}
14441444

14451445
[Fact]
1446-
public void ExpressionTests_NullPropagating_WithoutDefaultValue()
1446+
public void ExpressionTests_NullPropagating_NestedInteger_WithoutDefaultValue()
14471447
{
14481448
// Arrange
14491449
var testModels = User.GenerateSampleModels(2, true).ToList();
@@ -1459,7 +1459,35 @@ public void ExpressionTests_NullPropagating_WithoutDefaultValue()
14591459
}
14601460

14611461
[Fact]
1462-
public void ExpressionTests_NullPropagating_WithDefaultValue()
1462+
public void ExpressionTests_NullPropagating_NullableInteger_WithDefaultValue()
1463+
{
1464+
// Arrange
1465+
var testModels = User.GenerateSampleModels(2, true).ToList();
1466+
1467+
// Act
1468+
var result = testModels.AsQueryable().Select(t => t.NullableInt != null ? t.NullableInt : 100).ToArray();
1469+
var resultDynamic = testModels.AsQueryable().Select("np(NullableInt, 100)").ToDynamicArray<int>();
1470+
1471+
// Assert
1472+
Check.That(resultDynamic).ContainsExactly(result);
1473+
}
1474+
1475+
[Fact]
1476+
public void ExpressionTests_NullPropagating_String_WithDefaultValue()
1477+
{
1478+
// Arrange
1479+
var testModels = User.GenerateSampleModels(2, true).ToList();
1480+
1481+
// Act
1482+
var result = testModels.AsQueryable().Select(t => t.UserName != null ? t.UserName : "x").ToArray();
1483+
var resultDynamic = testModels.AsQueryable().Select("np(UserName, \"x\")").ToDynamicArray<string>();
1484+
1485+
// Assert
1486+
Check.That(resultDynamic).ContainsExactly(result);
1487+
}
1488+
1489+
[Fact]
1490+
public void ExpressionTests_NullPropagatingNested_WithDefaultValue()
14631491
{
14641492
// Arrange
14651493
var testModels = User.GenerateSampleModels(2, true).ToList();

0 commit comments

Comments
 (0)