Skip to content

Commit ea88769

Browse files
authored
fix (#342)
1 parent 1046418 commit ea88769

File tree

7 files changed

+66
-34
lines changed

7 files changed

+66
-34
lines changed

src/Microsoft.EntityFrameworkCore.DynamicLinq/Microsoft.EntityFrameworkCore.DynamicLinq.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
4646
<NugetTargetMoniker>UAP,Version=v10.0</NugetTargetMoniker>
4747
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
48-
<TargetPlatformVersion>10.0.14393.0</TargetPlatformVersion>
48+
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
4949
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
5050
<TargetFrameworkIdentifier>.NETCore</TargetFrameworkIdentifier>
5151
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
@@ -104,4 +104,4 @@
104104
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.0.10" />
105105
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.0" />
106106
</ItemGroup>
107-
</Project>
107+
</Project>

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

+13-6
Original file line numberDiff line numberDiff line change
@@ -244,11 +244,9 @@ private void WrapConstantExpressions(ref Expression left, ref Expression right)
244244
}
245245
}
246246

247-
public bool TryGenerateAndAlsoNotNullExpression(Expression sourceExpression, out Expression generatedExpression)
247+
public bool TryGenerateAndAlsoNotNullExpression(Expression sourceExpression, bool addSelf, out Expression generatedExpression)
248248
{
249-
generatedExpression = sourceExpression;
250-
251-
var expressions = CollectExpressions(sourceExpression);
249+
var expressions = CollectExpressions(addSelf, sourceExpression);
252250

253251
if (expressions.Count == 1 && !(expressions[0] is MethodCallExpression))
254252
{
@@ -305,11 +303,20 @@ private static Expression GetMemberExpression(Expression expression)
305303
return null;
306304
}
307305

308-
private static List<Expression> CollectExpressions(Expression sourceExpression)
306+
private static List<Expression> CollectExpressions(bool addSelf, Expression sourceExpression)
309307
{
310-
var list = new List<Expression>();
311308
Expression expression = GetMemberExpression(sourceExpression);
312309

310+
var list = new List<Expression>();
311+
312+
if (addSelf && expression is MemberExpression memberExpressionFirst)
313+
{
314+
if (TypeHelper.IsNullableType(memberExpressionFirst.Type) || !memberExpressionFirst.Type.GetTypeInfo().IsValueType)
315+
{
316+
list.Add(sourceExpression);
317+
}
318+
}
319+
313320
while (expression is MemberExpression memberExpression)
314321
{
315322
expression = GetMemberExpression(memberExpression.Expression);

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

+1-16
Original file line numberDiff line numberDiff line change
@@ -1138,26 +1138,11 @@ Expression ParseFunctionNullPropagation()
11381138
bool hasDefaultParameter = args.Length == 2;
11391139
Expression expressionIfFalse = hasDefaultParameter ? args[1] : Expression.Constant(null);
11401140

1141-
if (_expressionHelper.TryGenerateAndAlsoNotNullExpression(memberExpression, out Expression generatedExpression))
1141+
if (_expressionHelper.TryGenerateAndAlsoNotNullExpression(memberExpression, hasDefaultParameter, out Expression generatedExpression))
11421142
{
11431143
return GenerateConditional(generatedExpression, memberExpression, expressionIfFalse, errorPos);
11441144
}
11451145

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-
}
1151-
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);
1158-
}
1159-
1160-
// For non-nullable with default, just ignore default and return the single expression.
11611146
return memberExpression;
11621147
}
11631148

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ internal interface IExpressionHelper
2828

2929
Expression OptimizeStringForEqualityIfPossible(string text, Type type);
3030

31-
bool TryGenerateAndAlsoNotNullExpression(Expression sourceExpression, out Expression generatedExpression);
31+
bool TryGenerateAndAlsoNotNullExpression(Expression sourceExpression, bool addSelf, out Expression generatedExpression);
3232

3333
void WrapConstantExpression(ref Expression argument);
3434
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
4343
<NugetTargetMoniker>UAP,Version=v10.0</NugetTargetMoniker>
4444
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
45-
<TargetPlatformVersion>10.0.14393.0</TargetPlatformVersion>
45+
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
4646
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
4747
<TargetFrameworkIdentifier>.NETCore</TargetFrameworkIdentifier>
4848
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>

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

+10
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,9 @@ public void ExpressionTests_NullCoalescing()
13071307
}
13081308

13091309
[Theory]
1310+
[InlineData("np(str)", "Select(Param_0 => Param_0.str)")]
1311+
[InlineData("np(strNull)", "Select(Param_0 => Param_0.strNull)")]
1312+
[InlineData("np(str, \"x\")", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.str != null)), Param_0.str, \"x\"))")]
13101313
[InlineData("np(g)", "Select(Param_0 => Param_0.g)")]
13111314
[InlineData("np(gnullable)", "Select(Param_0 => Param_0.gnullable)")]
13121315
[InlineData("np(dt)", "Select(Param_0 => Param_0.dt)")]
@@ -1332,9 +1335,12 @@ public void ExpressionTests_NullCoalescing()
13321335
[InlineData("np(item.GuidNormal)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.item != null)), Convert(Param_0.item.GuidNormal, Nullable`1), null))")]
13331336
#endif
13341337

1338+
[InlineData("np(nested.strNull)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.nested != null)), Param_0.nested.strNull, null))")]
1339+
[InlineData("np(nested.strNull, \"x\")", "Select(Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.nested != null)) AndAlso (Param_0.nested.strNull != null)), Param_0.nested.strNull, \"x\"))")]
13351340
[InlineData("np(nested.gnullable)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.nested != null)), Param_0.nested.gnullable, null))")]
13361341
[InlineData("np(nested.dtnullable)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.nested != null)), Param_0.nested.dtnullable, null))")]
13371342
[InlineData("np(nested.nullablenumber)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.nested != null)), Param_0.nested.nullablenumber, null))")]
1343+
[InlineData("np(nested.nullablenumber, 42)", "Select(Param_0 => IIF((((Param_0 != null) AndAlso (Param_0.nested != null)) AndAlso (Param_0.nested.nullablenumber != null)), Param_0.nested.nullablenumber, 42))")]
13381344
[InlineData("np(nested._enumnullable)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.nested != null)), Param_0.nested._enumnullable, null))")]
13391345
[InlineData("np(item.GuidNull)", "Select(Param_0 => IIF(((Param_0 != null) AndAlso (Param_0.item != null)), Param_0.item.GuidNull, null))")]
13401346
public void ExpressionTests_NullPropagating(string test, string query)
@@ -1352,6 +1358,8 @@ public void ExpressionTests_NullPropagating(string test, string query)
13521358
_enumnullable = (TestEnum2?) TestEnum2.Var2,
13531359
number = 1,
13541360
nullablenumber = (int?) 2,
1361+
str = "str",
1362+
strNull = (string) null,
13551363
nested = new
13561364
{
13571365
g = Guid.NewGuid(),
@@ -1362,6 +1370,8 @@ public void ExpressionTests_NullPropagating(string test, string query)
13621370
_enumnullable = (TestEnum2?) TestEnum2.Var2,
13631371
number = 1,
13641372
nullablenumber = (int?) 2,
1373+
str = "str",
1374+
strNull = (string) null
13651375
},
13661376
item = new TestGuidNullClass
13671377
{

test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionHelperTests.cs

+38-8
Original file line numberDiff line numberDiff line change
@@ -85,31 +85,59 @@ public void ExpressionHelper_OptimizeStringForEqualityIfPossible_Guid_Invalid()
8585
}
8686

8787
[Fact]
88-
public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_ForMultiple()
88+
public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_NestedNonNullable()
8989
{
9090
// Assign
91-
Expression<Func<Item, int>> expression = (x) => x.Relation1.Relation2.Id;
91+
Expression<Func<Item, int>> expression = x => x.Relation1.Relation2.Id;
9292

9393
// Act
94-
bool result = _expressionHelper.TryGenerateAndAlsoNotNullExpression(expression, out Expression generatedExpresion);
94+
bool result = _expressionHelper.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression);
9595

9696
// Assert
9797
Check.That(result).IsTrue();
98-
Check.That(generatedExpresion.ToString()).IsEqualTo("(((x != null) AndAlso (x.Relation1 != null)) AndAlso (x.Relation1.Relation2 != null))");
98+
Check.That(generatedExpression.ToString()).IsEqualTo("(((x != null) AndAlso (x.Relation1 != null)) AndAlso (x.Relation1.Relation2 != null))");
9999
}
100100

101101
[Fact]
102-
public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_ForSingle()
102+
public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_NestedNullable_AddSelfFalse()
103103
{
104104
// Assign
105-
Expression<Func<Item, int>> expression = (x) => x.Id;
105+
Expression<Func<Item, int?>> expression = x => x.Relation1.Relation2.IdNullable;
106106

107107
// Act
108-
bool result = _expressionHelper.TryGenerateAndAlsoNotNullExpression(expression, out Expression generatedExpresion);
108+
bool result = _expressionHelper.TryGenerateAndAlsoNotNullExpression(expression, false, out Expression generatedExpression);
109+
110+
// Assert
111+
Check.That(result).IsTrue();
112+
Check.That(generatedExpression.ToString()).IsEqualTo("(((x != null) AndAlso (x.Relation1 != null)) AndAlso (x.Relation1.Relation2 != null))");
113+
}
114+
115+
[Fact]
116+
public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_NestedNullable_AddSelfTrue()
117+
{
118+
// Assign
119+
Expression<Func<Item, int?>> expression = x => x.Relation1.Relation2.IdNullable;
120+
121+
// Act
122+
bool result = _expressionHelper.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression);
123+
124+
// Assert
125+
Check.That(result).IsTrue();
126+
Check.That(generatedExpression.ToString()).IsEqualTo("((((x != null) AndAlso (x.Relation1 != null)) AndAlso (x.Relation1.Relation2 != null)) AndAlso (x => x.Relation1.Relation2.IdNullable != null))");
127+
}
128+
129+
[Fact]
130+
public void ExpressionHelper_TryGenerateAndAlsoNotNullExpression_NonNullable()
131+
{
132+
// Assign
133+
Expression<Func<Item, int>> expression = x => x.Id;
134+
135+
// Act
136+
bool result = _expressionHelper.TryGenerateAndAlsoNotNullExpression(expression, true, out Expression generatedExpression);
109137

110138
// Assert
111139
Check.That(result).IsFalse();
112-
Check.That(generatedExpresion.ToString()).IsEqualTo("x => x.Id");
140+
Check.That(generatedExpression.ToString()).IsEqualTo("x => x.Id");
113141
}
114142

115143
class Item
@@ -127,6 +155,8 @@ class Relation1
127155
class Relation2
128156
{
129157
public int Id { get; set; }
158+
159+
public int? IdNullable { get; set; }
130160
}
131161
}
132162
}

0 commit comments

Comments
 (0)