Skip to content

Commit 7de6e98

Browse files
authored
NullPropagation operator: support nullable DateTime (#464)
* DT * fx * fix ut net452 * . . . * cleanup code * merge
1 parent 5351719 commit 7de6e98

File tree

6 files changed

+73
-5
lines changed

6 files changed

+73
-5
lines changed

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,11 @@ public bool TryGenerateAndAlsoNotNullExpression(Expression sourceExpression, boo
257257
// Reverse the list
258258
expressions.Reverse();
259259

260-
// Convert all expressions into '!= null'
261-
var binaryExpressions = expressions.Select(expression => Expression.NotEqual(expression, Expression.Constant(null))).ToArray();
260+
// Convert all expressions into '!= null' expressions (only if the type can be null)
261+
var binaryExpressions = expressions
262+
.Where(expression => TypeHelper.TypeCanBeNull(expression.Type))
263+
.Select(expression => Expression.NotEqual(expression, Expression.Constant(null)))
264+
.ToArray();
262265

263266
// Convert all binary expressions into `AndAlso(...)`
264267
generatedExpression = binaryExpressions[0];

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

+7
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,13 @@ public static bool IsNullableType(Type type)
260260
return type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
261261
}
262262

263+
public static bool TypeCanBeNull(Type type)
264+
{
265+
Check.NotNull(type, nameof(type));
266+
267+
return !type.GetTypeInfo().IsValueType || IsNullableType(type);
268+
}
269+
263270
public static Type ToNullableType(Type type)
264271
{
265272
Check.NotNull(type, nameof(type));

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -1338,13 +1338,15 @@ public void ExpressionTests_NullCoalescing()
13381338
[InlineData("np(nested._enum)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.nested != null)), Convert(Param_0.nested._enum), null))")]
13391339
[InlineData("np(item.Id)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.item != null)), Convert(Param_0.item.Id), null))")]
13401340
[InlineData("np(item.GuidNormal)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.item != null)), Convert(Param_0.item.GuidNormal), null))")]
1341+
[InlineData("np(nested.dtnullable.Value.Year)", "Select(Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.nested != null)) AndAlso (Param_0.nested.dtnullable != null)), Convert(Param_0.nested.dtnullable.Value.Year), null))")]
13411342
#else
13421343
[InlineData("np(nested.g)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.nested != null)), Convert(Param_0.nested.g, Nullable`1), null))")]
13431344
[InlineData("np(nested.dt)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.nested != null)), Convert(Param_0.nested.dt, Nullable`1), null))")]
13441345
[InlineData("np(nested.number)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.nested != null)), Convert(Param_0.nested.number, Nullable`1), null))")]
13451346
[InlineData("np(nested._enum)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.nested != null)), Convert(Param_0.nested._enum, Nullable`1), null))")]
13461347
[InlineData("np(item.Id)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.item != null)), Convert(Param_0.item.Id, Nullable`1), null))")]
13471348
[InlineData("np(item.GuidNormal)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.item != null)), Convert(Param_0.item.GuidNormal, Nullable`1), null))")]
1349+
[InlineData("np(nested.dtnullable.Value.Year)", "Select(Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.nested != null)) AndAlso (Param_0.nested.dtnullable != null)), Convert(Param_0.nested.dtnullable.Value.Year, Nullable`1), null))")]
13481350
#endif
13491351

13501352
[InlineData("np(nested.strNull)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.nested != null)), Param_0.nested.strNull, null))")]
@@ -1502,8 +1504,8 @@ public void ExpressionTests_NullPropagation_NullableDateTime()
15021504
}.AsQueryable();
15031505

15041506
// Act
1505-
var result = q.OrderBy(x => x.date1).Select(x => x.id).ToArray();
1506-
var resultDynamic = q.OrderBy("np(date1)").Select("id").ToDynamicArray<int>();
1507+
var result = q.OrderBy(x => x.date1.Value.Year).Select(x => x.id).ToArray();
1508+
var resultDynamic = q.OrderBy("np(date1.Value.Year)").Select("id").ToDynamicArray<int>();
15071509

15081510
// Assert
15091511
Check.That(resultDynamic).ContainsExactly(result);

test/System.Linq.Dynamic.Core.Tests/QueryableTests.GroupBy.cs

+55
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,19 @@
55
using System.Reflection;
66
using Newtonsoft.Json;
77
using Xunit;
8+
using FluentAssertions;
89

910
namespace System.Linq.Dynamic.Core.Tests
1011
{
1112
public partial class QueryableTests
1213
{
14+
public class DateTimeTest
15+
{
16+
public DateTimeTest Test { get; set; }
17+
18+
public DateTime? D { get; set; }
19+
}
20+
1321
[Fact]
1422
public void GroupBy_Dynamic()
1523
{
@@ -26,6 +34,53 @@ public void GroupBy_Dynamic()
2634
Assert.Equal(testList.GroupBy(x => x.Profile.Age).Count(), byAgeReturnAll.Count());
2735
}
2836

37+
[Fact]
38+
public void GroupBy_Dynamic_NullableDateTime()
39+
{
40+
// Arrange
41+
var qry = new[] { new DateTime(2020, 1, 1), (DateTime?)null }.AsQueryable();
42+
43+
// Act
44+
var byYear = qry.GroupBy("np(Value.Year, 2019)");
45+
46+
// Assert
47+
byYear.Should().HaveCount(2);
48+
}
49+
50+
[Fact]
51+
public void GroupBy_Dynamic_NullableDateTime_2Levels()
52+
{
53+
// Arrange
54+
var qry = new[]
55+
{
56+
new DateTimeTest { Test = new DateTimeTest { D = new DateTime(2020, 1, 1) } },
57+
new DateTimeTest { Test = null }
58+
}.AsQueryable();
59+
60+
// Act
61+
var byYear = qry.GroupBy("np(Test.D.Value.Year, 2019)");
62+
63+
// Assert
64+
byYear.Should().HaveCount(2);
65+
}
66+
67+
[Fact]
68+
public void GroupBy_Dynamic_NullableDateTime_3Levels()
69+
{
70+
// Arrange
71+
var qry = new[]
72+
{
73+
new DateTimeTest { Test = new DateTimeTest { Test = new DateTimeTest { D = new DateTime(2020, 1, 1) } } },
74+
new DateTimeTest { Test = null }
75+
}.AsQueryable();
76+
77+
// Act
78+
var byYear = qry.GroupBy("np(Test.Test.D.Value.Year, 2019)");
79+
80+
// Assert
81+
byYear.Should().HaveCount(2);
82+
}
83+
2984
// https://github.com/StefH/System.Linq.Dynamic.Core/issues/75
3085
[Fact]
3186
public void GroupBy_Dynamic_Issue75()

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

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<PropertyGroup>
33
<Authors>Stef Heyenrath</Authors>
44
<TargetFrameworks>net452;netcoreapp3.1</TargetFrameworks>
5+
<!--<TargetFramework>netcoreapp3.1</TargetFramework>-->
56
<AssemblyName>System.Linq.Dynamic.Core.Tests</AssemblyName>
67
<DebugType>full</DebugType>
78
<SignAssembly>True</SignAssembly>

version.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<Project>
22
<PropertyGroup>
3-
<PatchVersion>7-preview-01</PatchVersion>
3+
<PatchVersion>7-preview-02</PatchVersion>
44
</PropertyGroup>
55
</Project>

0 commit comments

Comments
 (0)