Skip to content

Commit 8874662

Browse files
StefHCopilot
andauthored
Fix ExpressionHelper.TryConvertTypes to generate correct Convert in case left or right is null (#954)
* Fix ExpressionHelper.TryConvertTypes to generate correct Convert in case left or right is null * opt * Update test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs Co-authored-by: Copilot <[email protected]> * . --------- Co-authored-by: Copilot <[email protected]>
1 parent 9f59211 commit 8874662

File tree

6 files changed

+121
-15
lines changed

6 files changed

+121
-15
lines changed

src/System.Linq.Dynamic.Core.NewtonsoftJson/NewtonsoftJsonExtensions.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ public static JArray Where(this JArray source, NewtonsoftJsonParsingConfig confi
821821

822822
if (source.Count == 0)
823823
{
824-
return new JArray();
824+
return [];
825825
}
826826

827827
var queryable = ToQueryable(source, config);
@@ -848,7 +848,8 @@ public static JArray Where(this JArray source, NewtonsoftJsonParsingConfig confi
848848
private static JArray ToJArray(Func<IQueryable> func)
849849
{
850850
var array = new JArray();
851-
foreach (var dynamicElement in func())
851+
var funcResult = func();
852+
foreach (var dynamicElement in funcResult)
852853
{
853854
var element = dynamicElement switch
854855
{

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,12 @@ public bool ExpressionQualifiesForNullPropagation(Expression? expression)
361361
public Expression GenerateDefaultExpression(Type type)
362362
{
363363
#if NET35
364-
return Expression.Constant(Activator.CreateInstance(type));
364+
if (type.IsValueType)
365+
{
366+
return Expression.Constant(Activator.CreateInstance(type), type);
367+
}
368+
369+
return Expression.Constant(null, type);
365370
#else
366371
return Expression.Default(type);
367372
#endif
@@ -388,11 +393,19 @@ public bool TryConvertTypes(ref Expression left, ref Expression right)
388393

389394
if (left.Type == typeof(object))
390395
{
391-
left = Expression.Convert(left, right.Type);
396+
left = Expression.Condition(
397+
Expression.Equal(left, Expression.Constant(null, typeof(object))),
398+
GenerateDefaultExpression(right.Type),
399+
Expression.Convert(left, right.Type)
400+
);
392401
}
393402
else if (right.Type == typeof(object))
394403
{
395-
right = Expression.Convert(right, left.Type);
404+
right = Expression.Condition(
405+
Expression.Equal(right, Expression.Constant(null, typeof(object))),
406+
GenerateDefaultExpression(left.Type),
407+
Expression.Convert(right, left.Type)
408+
);
396409
}
397410

398411
return true;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,5 @@ internal interface IExpressionHelper
5252
/// <summary>
5353
/// If the types are different (and not null), try to convert the object type to other type.
5454
/// </summary>
55-
public bool TryConvertTypes(ref Expression left, ref Expression right);
55+
bool TryConvertTypes(ref Expression left, ref Expression right);
5656
}

test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using FluentAssertions;
1+
using System.Linq.Dynamic.Core.NewtonsoftJson.Config;
2+
using FluentAssertions;
23
using Newtonsoft.Json.Linq;
34
using Xunit;
45

@@ -506,4 +507,62 @@ public void Where_With_Select()
506507
var first = result.First();
507508
first.Value<string>().Should().Be("Doe");
508509
}
510+
511+
//[Fact]
512+
//public void Where_OptionalProperty()
513+
//{
514+
// // Arrange
515+
// var config = new NewtonsoftJsonParsingConfig
516+
// {
517+
// ConvertObjectToSupportComparison = true
518+
// };
519+
// var array =
520+
// """
521+
// [
522+
// {
523+
// "Name": "John",
524+
// "Age": 30
525+
// },
526+
// {
527+
// "Name": "Doe"
528+
// }
529+
// ]
530+
// """;
531+
532+
// // Act
533+
// var result = JArray.Parse(array).Where(config, "Age > 30").Select("Name");
534+
535+
// // Assert
536+
// result.Should().HaveCount(1);
537+
// var first = result.First();
538+
// first.Value<string>().Should().Be("John");
539+
//}
540+
541+
[Theory]
542+
[InlineData("notExisting == true")]
543+
[InlineData("notExisting == \"true\"")]
544+
[InlineData("notExisting == 1")]
545+
[InlineData("notExisting == \"1\"")]
546+
[InlineData("notExisting == \"something\"")]
547+
[InlineData("notExisting > 1")]
548+
[InlineData("true == notExisting")]
549+
[InlineData("\"true\" == notExisting")]
550+
[InlineData("1 == notExisting")]
551+
[InlineData("\"1\" == notExisting")]
552+
[InlineData("\"something\" == notExisting")]
553+
[InlineData("1 < notExisting")]
554+
public void Where_NonExistingMember_EmptyResult(string predicate)
555+
{
556+
// Arrange
557+
var config = new NewtonsoftJsonParsingConfig
558+
{
559+
ConvertObjectToSupportComparison = true
560+
};
561+
562+
// Act
563+
var result = _source.Where(config, predicate);
564+
565+
// Assert
566+
result.Should().BeEmpty();
567+
}
509568
}

test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Text.Json;
1+
using System.Linq.Dynamic.Core.SystemTextJson.Config;
2+
using System.Text.Json;
23
using FluentAssertions;
34
using Xunit;
45

@@ -535,4 +536,32 @@ public void Where_With_Select()
535536
array.Should().HaveCount(1);
536537
array.First().GetString().Should().Be("Doe");
537538
}
539+
540+
[Theory]
541+
[InlineData("notExisting == true")]
542+
[InlineData("notExisting == \"true\"")]
543+
[InlineData("notExisting == 1")]
544+
[InlineData("notExisting == \"1\"")]
545+
[InlineData("notExisting == \"something\"")]
546+
[InlineData("notExisting > 1")]
547+
[InlineData("true == notExisting")]
548+
[InlineData("\"true\" == notExisting")]
549+
[InlineData("1 == notExisting")]
550+
[InlineData("\"1\" == notExisting")]
551+
[InlineData("\"something\" == notExisting")]
552+
[InlineData("1 < notExisting")]
553+
public void Where_NonExistingMember_EmptyResult(string predicate)
554+
{
555+
// Arrange
556+
var config = new SystemTextJsonParsingConfig
557+
{
558+
ConvertObjectToSupportComparison = true
559+
};
560+
561+
// Act
562+
var result = _source.Where(config, predicate).RootElement.EnumerateArray();
563+
564+
// Assert
565+
result.Should().BeEmpty();
566+
}
538567
}

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,20 +131,24 @@ public void DynamicClass_GettingValue_ByIndex_Should_Work()
131131
public void DynamicClass_SettingExistingPropertyValue_ByIndex_Should_Work()
132132
{
133133
// Arrange
134-
var test = "Test";
135-
var newTest = "abc";
136-
var range = new List<object>
134+
var originalValue = "Test";
135+
var newValue = "abc";
136+
var array = new object[]
137137
{
138-
new { FieldName = test, Value = 3.14159 }
138+
new
139+
{
140+
FieldName = originalValue,
141+
Value = 3.14159
142+
}
139143
};
140144

141145
// Act
142-
var rangeResult = range.AsQueryable().Select("new(FieldName as FieldName)").ToDynamicList();
146+
var rangeResult = array.AsQueryable().Select("new(FieldName as FieldName)").ToDynamicList();
143147
var item = rangeResult.First();
144148

145-
item["FieldName"] = newTest;
149+
item["FieldName"] = newValue;
146150
var value = item["FieldName"] as string;
147-
value.Should().Be(newTest);
151+
value.Should().Be(newValue);
148152
}
149153

150154
[Fact]

0 commit comments

Comments
 (0)