diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f998851e..79f0fba2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -22,13 +22,13 @@ jobs:
- uses: actions/setup-dotnet@v4
with:
- dotnet-version: '9.0.x'
+ dotnet-version: '10.0.x'
- name: Build
run: |
dotnet build ./src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj -c Release -p:buildType=azure-pipelines-ci
- - name: Run Tests EFCore net9.0
+ - name: Run Tests EFCore net10.0
run: |
dotnet test ./test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj -c Release -p:buildType=azure-pipelines-ci
@@ -76,39 +76,24 @@ jobs:
- name: Build
run: |
dotnet build ./src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj -c Debug -p:buildType=azure-pipelines-ci
-
- - name: Run Tests EF net8.0 (with Coverage)
+
+ - name: Run Tests EFCore .NET 10 (with Coverage)
run: |
- dotnet-coverage collect 'dotnet test ./test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj --configuration Debug --framework net8.0 -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-ef.xml
+ dotnet-coverage collect 'dotnet test ./test/System.Linq.Dynamic.Core.Tests/System.Linq.Dynamic.Core.Tests.csproj --configuration Debug -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-efcore.xml
- - name: Run Tests EFCore net8.0 (with Coverage)
+ - name: Run Tests EF .NET 10 (with Coverage)
run: |
- dotnet-coverage collect 'dotnet test ./test/System.Linq.Dynamic.Core.Tests.Net8/System.Linq.Dynamic.Core.Tests.Net8.csproj --configuration Debug --framework net8.0 -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-efcore.xml
+ dotnet-coverage collect 'dotnet test ./test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj --configuration Debug --framework net10.0 -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-ef.xml
- - name: Run Tests Newtonsoft.Json .NET 8 (with Coverage)
+ - name: Run Tests Newtonsoft.Json .NET 10 (with Coverage)
run: |
- dotnet-coverage collect 'dotnet test ./test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj --configuration Debug --framework net8.0 -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-newtonsoftjson.xml
+ dotnet-coverage collect 'dotnet test ./test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj --configuration Debug --framework net10.0 -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-newtonsoftjson.xml
- - name: Run Tests System.Text.Json .NET 8 (with Coverage)
+ - name: Run Tests System.Text.Json .NET 10 (with Coverage)
run: |
- dotnet-coverage collect 'dotnet test ./test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj --configuration Debug --framework net8.0 -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-systemtextjson.xml
+ dotnet-coverage collect 'dotnet test ./test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj --configuration Debug --framework net10.0 -p:buildType=azure-pipelines-ci' -f xml -o dynamic-coverage-systemtextjson.xml
- name: End analysis on SonarCloud
if: ${{ steps.secret-check.outputs.run_analysis == 'true' }}
run: |
- dotnet sonarscanner end /d:sonar.token=${{ secrets.SONAR_TOKEN }}
-
- # - name: Run Tests EFCore net8.0
- # run: |
- # dotnet test ./test/System.Linq.Dynamic.Core.Tests.Net7/System.Linq.Dynamic.Core.Tests.Net8.csproj -c Release -p:buildType=azure-pipelines-ci
- # continue-on-error: true
-
- # - name: Run Tests EFCore net7.0
- # run: |
- # dotnet test ./test/System.Linq.Dynamic.Core.Tests.Net7/System.Linq.Dynamic.Core.Tests.Net7.csproj -c Release -p:buildType=azure-pipelines-ci
- # continue-on-error: true
-
- # - name: Run Tests EFCore net6.0
- # run: |
- # dotnet test ./test/System.Linq.Dynamic.Core.Tests.Net6/System.Linq.Dynamic.Core.Tests.Net6.csproj -c Release -p:buildType=azure-pipelines-ci
- # continue-on-error: true
\ No newline at end of file
+ dotnet sonarscanner end /d:sonar.token=${{ secrets.SONAR_TOKEN }}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 210e2f20..4a4ae9bc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,23 @@
-# v1.6.9 (10 October 2025)
+# v1.7.1 (29 November 2025)
+- [#961](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/961) - Fix Json when property value is null [bug] contributed by [StefH](https://github.com/StefH)
+- [#962](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/962) - json: fix logic when property is not found [bug] contributed by [StefH](https://github.com/StefH)
+- [#965](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/965) - Fix NumberParser for integer < int.MinValue [bug] contributed by [StefH](https://github.com/StefH)
+- [#960](https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/960) - json: follow up for not existing members [bug]
+- [#964](https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/964) - Integer numbers smaller than int.MinValue are not parsed correctly [bug]
+
+# v1.7.0 (15 November 2025)
+- [#956](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/956) - Fix parsing Hex and Binary [bug] contributed by [StefH](https://github.com/StefH)
+- [#957](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/957) - .NET 10 [feature] contributed by [StefH](https://github.com/StefH)
+- [#958](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/958) - Support normalization of objects for Z.DynamicLinq.Json [feature] contributed by [StefH](https://github.com/StefH)
+- [#955](https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/955) - Hexadecimal und binary literals sometimes are interpreted as decimal [bug]
+
+# v1.6.10 (08 November 2025)
+- [#953](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/953) - Fixed adding Enum and integer [bug] contributed by [StefH](https://github.com/StefH)
+- [#954](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/954) - Fix ExpressionHelper.TryConvertTypes to generate correct Convert in case left or right is null [bug] contributed by [StefH](https://github.com/StefH)
+- [#951](https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/951) - Parsing error adding numeric constant to enum value [bug]
+- [#952](https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/952) - Json: How to handle not existing member [bug]
+
+# v1.6.9 (11 October 2025)
- [#950](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/950) - DynamicExpressionParser - Handle indexed properties with any number of indices in expression [bug] contributed by [thibault-reigner](https://github.com/thibault-reigner)
# v1.6.8 (28 September 2025)
diff --git a/Generate-ReleaseNotes.bat b/Generate-ReleaseNotes.bat
index aa50344d..809d22d8 100644
--- a/Generate-ReleaseNotes.bat
+++ b/Generate-ReleaseNotes.bat
@@ -1,5 +1,5 @@
rem https://github.com/StefH/GitHubReleaseNotes
-SET version=v1.6.9
+SET version=v1.7.1
GitHubReleaseNotes --output CHANGELOG.md --exclude-labels known_issue out_of_scope not_planned invalid question documentation wontfix environment duplicate --language en --version %version% --token %GH_TOKEN%
diff --git a/System.Linq.Dynamic.Core.sln b/System.Linq.Dynamic.Core.sln
index 4bfd1ffa..7aaa3982 100644
--- a/System.Linq.Dynamic.Core.sln
+++ b/System.Linq.Dynamic.Core.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0.31606.5
+# Visual Studio Version 18
+VisualStudioVersion = 18.0.11205.157 d18.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{8463ED7E-69FB-49AE-85CF-0791AFD98E38}"
ProjectSection(SolutionItems) = preProject
@@ -161,6 +161,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Linq.Dynamic.Core.Te
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleAppPerformanceTest", "src-console\ConsoleAppPerformanceTest\ConsoleAppPerformanceTest.csproj", "{067C00CF-29FA-4643-814D-3A3C3C84634F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp_net10", "src-console\ConsoleApp_net10\ConsoleApp_net10.csproj", "{34C58129-07DB-287E-A29B-FA8EE8BAEB05}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.EntityFrameworkCore.DynamicLinq.EFCore10", "src\Microsoft.EntityFrameworkCore.DynamicLinq.EFCore10\Microsoft.EntityFrameworkCore.DynamicLinq.EFCore10.csproj", "{1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Linq.Dynamic.Core.Tests.Net9", "test\System.Linq.Dynamic.Core.Tests.Net9\System.Linq.Dynamic.Core.Tests.Net9.csproj", "{00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1057,6 +1063,54 @@ Global
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Release|x64.Build.0 = Release|Any CPU
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Release|x86.ActiveCfg = Release|Any CPU
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Release|x86.Build.0 = Release|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Debug|ARM.Build.0 = Debug|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Debug|x64.Build.0 = Debug|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Debug|x86.Build.0 = Debug|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Release|Any CPU.Build.0 = Release|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Release|ARM.ActiveCfg = Release|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Release|ARM.Build.0 = Release|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Release|x64.ActiveCfg = Release|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Release|x64.Build.0 = Release|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Release|x86.ActiveCfg = Release|Any CPU
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05}.Release|x86.Build.0 = Release|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Debug|ARM.Build.0 = Debug|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Debug|x64.Build.0 = Debug|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Debug|x86.Build.0 = Debug|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Release|ARM.ActiveCfg = Release|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Release|ARM.Build.0 = Release|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Release|x64.ActiveCfg = Release|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Release|x64.Build.0 = Release|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Release|x86.ActiveCfg = Release|Any CPU
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}.Release|x86.Build.0 = Release|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Debug|ARM.Build.0 = Debug|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Debug|x64.Build.0 = Debug|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Debug|x86.Build.0 = Debug|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Release|ARM.ActiveCfg = Release|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Release|ARM.Build.0 = Release|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Release|x64.ActiveCfg = Release|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Release|x64.Build.0 = Release|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Release|x86.ActiveCfg = Release|Any CPU
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1117,6 +1171,9 @@ Global
{C774DAE7-54A0-4FCD-A3B7-3CB63D7E112D} = {DBD7D9B6-FCC7-4650-91AF-E6457573A68F}
{CEBE3A33-4814-42A4-BD8E-F7F2308A4C8C} = {8463ED7E-69FB-49AE-85CF-0791AFD98E38}
{067C00CF-29FA-4643-814D-3A3C3C84634F} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62}
+ {34C58129-07DB-287E-A29B-FA8EE8BAEB05} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62}
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520} = {DBD7D9B6-FCC7-4650-91AF-E6457573A68F}
+ {00C5928F-3846-5C1A-6AFB-DFD2149EA3E2} = {8463ED7E-69FB-49AE-85CF-0791AFD98E38}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {94C56722-194E-4B8B-BC23-B3F754E89A20}
diff --git a/src-console/ConsoleAppEF2.0.2_InMemory/ConsoleApp_netcore2.0_EF2.0.2_InMemory.csproj b/src-console/ConsoleAppEF2.0.2_InMemory/ConsoleApp_netcore2.0_EF2.0.2_InMemory.csproj
index 3a5d7189..9fd2818d 100644
--- a/src-console/ConsoleAppEF2.0.2_InMemory/ConsoleApp_netcore2.0_EF2.0.2_InMemory.csproj
+++ b/src-console/ConsoleAppEF2.0.2_InMemory/ConsoleApp_netcore2.0_EF2.0.2_InMemory.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/src-console/ConsoleAppEF2.0/ConsoleApp_netcore2.0_EF2.0.1.csproj b/src-console/ConsoleAppEF2.0/ConsoleApp_netcore2.0_EF2.0.1.csproj
index c019880d..ac0fd8be 100644
--- a/src-console/ConsoleAppEF2.0/ConsoleApp_netcore2.0_EF2.0.1.csproj
+++ b/src-console/ConsoleAppEF2.0/ConsoleApp_netcore2.0_EF2.0.1.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/src-console/ConsoleAppEF2.1.1/ConsoleApp_netcore2.1_EF2.1.1.csproj b/src-console/ConsoleAppEF2.1.1/ConsoleApp_netcore2.1_EF2.1.1.csproj
index f31529c9..71257ddb 100644
--- a/src-console/ConsoleAppEF2.1.1/ConsoleApp_netcore2.1_EF2.1.1.csproj
+++ b/src-console/ConsoleAppEF2.1.1/ConsoleApp_netcore2.1_EF2.1.1.csproj
@@ -18,7 +18,7 @@
-
+
diff --git a/src-console/ConsoleAppEF2.1/ConsoleApp_netcore2.0_EF2.1.csproj b/src-console/ConsoleAppEF2.1/ConsoleApp_netcore2.0_EF2.1.csproj
index 137df235..c30d8bda 100644
--- a/src-console/ConsoleAppEF2.1/ConsoleApp_netcore2.0_EF2.1.csproj
+++ b/src-console/ConsoleAppEF2.1/ConsoleApp_netcore2.0_EF2.1.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/src-console/ConsoleAppEF3.1/ConsoleApp_netcore3.1_EF3.1.csproj b/src-console/ConsoleAppEF3.1/ConsoleApp_netcore3.1_EF3.1.csproj
index 0f71601c..1d297cd3 100644
--- a/src-console/ConsoleAppEF3.1/ConsoleApp_netcore3.1_EF3.1.csproj
+++ b/src-console/ConsoleAppEF3.1/ConsoleApp_netcore3.1_EF3.1.csproj
@@ -20,7 +20,7 @@
-
+
diff --git a/src-console/ConsoleAppEF5/ConsoleApp_net5.0_EF5.csproj b/src-console/ConsoleAppEF5/ConsoleApp_net5.0_EF5.csproj
index 1fa72fbb..bc4122af 100644
--- a/src-console/ConsoleAppEF5/ConsoleApp_net5.0_EF5.csproj
+++ b/src-console/ConsoleAppEF5/ConsoleApp_net5.0_EF5.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/src-console/ConsoleAppEF5_InMemory/ConsoleApp_net5.0_EF5_InMemory.csproj b/src-console/ConsoleAppEF5_InMemory/ConsoleApp_net5.0_EF5_InMemory.csproj
index 0d37e480..69742229 100644
--- a/src-console/ConsoleAppEF5_InMemory/ConsoleApp_net5.0_EF5_InMemory.csproj
+++ b/src-console/ConsoleAppEF5_InMemory/ConsoleApp_net5.0_EF5_InMemory.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/src-console/ConsoleAppEF6_InMemory/ConsoleApp_net6.0_EF6_InMemory.csproj b/src-console/ConsoleAppEF6_InMemory/ConsoleApp_net6.0_EF6_InMemory.csproj
index e7a8b0ee..5db0eb28 100644
--- a/src-console/ConsoleAppEF6_InMemory/ConsoleApp_net6.0_EF6_InMemory.csproj
+++ b/src-console/ConsoleAppEF6_InMemory/ConsoleApp_net6.0_EF6_InMemory.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/src-console/ConsoleAppEF6_Sqlite/ConsoleApp_net6.0_EF6_Sqlite.csproj b/src-console/ConsoleAppEF6_Sqlite/ConsoleApp_net6.0_EF6_Sqlite.csproj
index 9f54be59..a4203e3d 100644
--- a/src-console/ConsoleAppEF6_Sqlite/ConsoleApp_net6.0_EF6_Sqlite.csproj
+++ b/src-console/ConsoleAppEF6_Sqlite/ConsoleApp_net6.0_EF6_Sqlite.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/src-console/ConsoleApp_net10/ConsoleApp_net10.csproj b/src-console/ConsoleApp_net10/ConsoleApp_net10.csproj
new file mode 100644
index 00000000..8c348bf9
--- /dev/null
+++ b/src-console/ConsoleApp_net10/ConsoleApp_net10.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Exe
+ net10.0
+ ConsoleApp
+ enable
+ latest
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src-console/ConsoleApp_net10/DataColumnOrdinalIgnoreCaseComparer.cs b/src-console/ConsoleApp_net10/DataColumnOrdinalIgnoreCaseComparer.cs
new file mode 100644
index 00000000..e1774a82
--- /dev/null
+++ b/src-console/ConsoleApp_net10/DataColumnOrdinalIgnoreCaseComparer.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections;
+
+namespace ConsoleApp_net6._0;
+
+public class DataColumnOrdinalIgnoreCaseComparer : IComparer
+{
+ public int Compare(object? x, object? y)
+ {
+ if (x == null && y == null)
+ {
+ return 0;
+ }
+
+ if (x == null)
+ {
+ return -1;
+ }
+
+ if (y == null)
+ {
+ return 1;
+ }
+
+ if (x is string xAsString && y is string yAsString)
+ {
+ return StringComparer.OrdinalIgnoreCase.Compare(xAsString, yAsString);
+ }
+
+ return Comparer.Default.Compare(x, y);
+ }
+}
\ No newline at end of file
diff --git a/src-console/ConsoleApp_net10/Program.cs b/src-console/ConsoleApp_net10/Program.cs
new file mode 100644
index 00000000..6108ceb0
--- /dev/null
+++ b/src-console/ConsoleApp_net10/Program.cs
@@ -0,0 +1,299 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Linq.Dynamic.Core;
+using System.Linq.Dynamic.Core.NewtonsoftJson;
+using System.Linq.Dynamic.Core.SystemTextJson;
+using System.Linq.Expressions;
+using System.Text.Json;
+using ConsoleApp_net6._0;
+using Newtonsoft.Json.Linq;
+
+namespace ConsoleApp;
+
+public class X
+{
+ public string Key { get; set; } = null!;
+
+ public List? Contestants { get; set; }
+}
+
+public class Y
+{
+}
+
+public class SalesData
+{
+ public string Region { get; set; }
+ public string Product { get; set; }
+ public string Sales { get; set; }
+}
+
+public class GroupedSalesData
+{
+ public string Region { get; set; }
+ public string? Product { get; set; }
+ public int TotalSales { get; set; }
+ public int GroupLevel { get; set; }
+}
+
+class Program
+{
+ static void Main(string[] args)
+ {
+ Issue918();
+ return;
+
+ Issue912a();
+ Issue912b();
+ return;
+
+ Json();
+ NewtonsoftJson();
+
+ return;
+
+ Issue389DoesNotWork();
+ return;
+ Issue389_Works();
+ return;
+
+ var q = new[]
+ {
+ new X { Key = "x" },
+ new X { Key = "a" },
+ new X { Key = "a", Contestants = new List { new() } }
+ }.AsQueryable();
+ var groupByKey = q.GroupBy("Key");
+ var selectQry = groupByKey.Select("new (Key, Sum(np(Contestants.Count, 0)) As TotalCount)").ToDynamicList();
+
+ Normal();
+ Dynamic();
+ }
+
+ private static void Issue918()
+ {
+ var persons = new DataTable();
+ persons.Columns.Add("FirstName", typeof(string));
+ persons.Columns.Add("Nickname", typeof(string));
+ persons.Columns.Add("Income", typeof(decimal)).AllowDBNull = true;
+
+ // Adding sample data to the first DataTable
+ persons.Rows.Add("alex", DBNull.Value, 5000.50m);
+ persons.Rows.Add("MAGNUS", "Mag", 5000.50m);
+ persons.Rows.Add("Terry", "Ter", 4000.20m);
+ persons.Rows.Add("Charlotte", "Charl", DBNull.Value);
+
+ var linqQuery =
+ from personsRow in persons.AsEnumerable()
+ select personsRow;
+
+ var queryableRows = linqQuery.AsQueryable();
+
+ // Sorted at the top of the list
+ var comparer = new DataColumnOrdinalIgnoreCaseComparer();
+ var sortedRows = queryableRows.OrderBy("FirstName", comparer).ToList();
+
+ int xxx = 0;
+ }
+
+ private static void Issue912a()
+ {
+ var extractedRows = new List
+ {
+ new() { Region = "North", Product = "Widget", Sales = "100" },
+ new() { Region = "North", Product = "Gadget", Sales = "150" },
+ new() { Region = "South", Product = "Widget", Sales = "200" },
+ new() { Region = "South", Product = "Gadget", Sales = "100" },
+ new() { Region = "North", Product = "Widget", Sales = "50" }
+ };
+
+ var rows = extractedRows.AsQueryable();
+
+ // GROUPING SET 1: (Region, Product)
+ var detailed = rows
+ .GroupBy("new (Region, Product)")
+ .Select("new (Key.Region as Region, Key.Product as Product, Sum(Convert.ToInt32(Sales)) as TotalSales, 0 as GroupLevel)");
+
+ // GROUPING SET 2: (Region)
+ var regionSubtotal = rows
+ .GroupBy("Region")
+ .Select("new (Key as Region, null as Product, Sum(Convert.ToInt32(Sales)) as TotalSales, 1 as GroupLevel)");
+
+ var combined = detailed.Concat(regionSubtotal).AsQueryable();
+ var ordered = combined.OrderBy("Product").ToDynamicList();
+
+ int x = 9;
+ }
+
+ private static void Issue912b()
+ {
+ var eInfoJoinTable = new DataTable();
+ eInfoJoinTable.Columns.Add("Region", typeof(string));
+ eInfoJoinTable.Columns.Add("Product", typeof(string));
+ eInfoJoinTable.Columns.Add("Sales", typeof(int));
+
+ eInfoJoinTable.Rows.Add("North", "Apples", 100);
+ eInfoJoinTable.Rows.Add("North", "Oranges", 150);
+ eInfoJoinTable.Rows.Add("South", "Apples", 200);
+ eInfoJoinTable.Rows.Add("South", "Oranges", 250);
+
+ var extractedRows =
+ from row in eInfoJoinTable.AsEnumerable()
+ select row;
+
+ var rows = extractedRows.AsQueryable();
+
+ // GROUPING SET 1: (Region, Product)
+ var detailed = rows
+ .GroupBy("new (Region, Product)")
+ .Select("new (Key.Region as Region, Key.Product as Product, Sum(Convert.ToInt32(Sales)) as TotalSales, 0 as GroupLevel)");
+
+ // GROUPING SET 2: (Region)
+ var regionSubtotal = rows
+ .GroupBy("Region")
+ .Select("new (Key as Region, null as Product, Sum(Convert.ToInt32(Sales)) as TotalSales, 1 as GroupLevel)");
+
+ var combined = detailed.ToDynamicArray().Concat(regionSubtotal.ToDynamicArray()).AsQueryable();
+ var ordered = combined.OrderBy("Product").ToDynamicList();
+
+ int x = 9;
+ }
+
+ private static void NewtonsoftJson()
+ {
+ var array = JArray.Parse(@"[
+ {
+ ""first"": 1,
+ ""City"": ""Paris"",
+ ""third"": ""test""
+ },
+ {
+ ""first"": 2,
+ ""City"": ""New York"",
+ ""third"": ""abc""
+ }]");
+
+ var where = array.Where("City == @0", "Paris");
+ foreach (var result in where)
+ {
+ Console.WriteLine(result["first"]);
+ }
+
+ var select = array.Select("City");
+ foreach (var result in select)
+ {
+ Console.WriteLine(result);
+ }
+
+ var whereWithSelect = array.Where("City == @0", "Paris").Select("first");
+ foreach (var result in whereWithSelect)
+ {
+ Console.WriteLine(result);
+ }
+ }
+
+ private static void Json()
+ {
+ var doc = JsonDocument.Parse(@"[
+ {
+ ""first"": 1,
+ ""City"": ""Paris"",
+ ""third"": ""test""
+ },
+ {
+ ""first"": 2,
+ ""City"": ""New York"",
+ ""third"": ""abc""
+ }]");
+
+ var where = doc.Where("City == @0", "Paris");
+ foreach (var result in where.RootElement.EnumerateArray())
+ {
+ Console.WriteLine(result.GetProperty("first"));
+ }
+
+ var select = doc.Select("City");
+ foreach (var result in select.RootElement.EnumerateArray())
+ {
+ Console.WriteLine(result);
+ }
+
+ var whereWithSelect = doc.Where("City == @0", "Paris").Select("first");
+ foreach (var result in whereWithSelect.RootElement.EnumerateArray())
+ {
+ Console.WriteLine(result);
+ }
+ }
+
+ private static void Issue389_Works()
+ {
+ var strArray = new[] { "1", "2", "3", "4" };
+ var x = new List();
+ x.Add(Expression.Parameter(strArray.GetType(), "strArray"));
+
+ string query = "string.Join(\",\", strArray)";
+
+ var e = DynamicExpressionParser.ParseLambda(x.ToArray(), null, query);
+ Delegate del = e.Compile();
+ var result1 = del.DynamicInvoke(new object?[] { strArray });
+ Console.WriteLine(result1);
+ }
+
+ private static void Issue389WorksWithInts()
+ {
+ var intArray = new object[] { 1, 2, 3, 4 };
+ var x = new List();
+ x.Add(Expression.Parameter(intArray.GetType(), "intArray"));
+
+ string query = "string.Join(\",\", intArray)";
+
+ var e = DynamicExpressionParser.ParseLambda(x.ToArray(), null, query);
+ Delegate del = e.Compile();
+ var result = del.DynamicInvoke(new object?[] { intArray });
+
+ Console.WriteLine(result);
+ }
+
+ private static void Issue389DoesNotWork()
+ {
+ var intArray = new[] { 1, 2, 3, 4 };
+ var x = new List();
+ x.Add(Expression.Parameter(intArray.GetType(), "intArray"));
+
+ string query = "string.Join(\",\", intArray)";
+
+ var e = DynamicExpressionParser.ParseLambda(x.ToArray(), null, query);
+ Delegate del = e.Compile();
+ var result = del.DynamicInvoke(new object?[] { intArray });
+
+ Console.WriteLine(result);
+ }
+
+ private static void Normal()
+ {
+ var e = new int[0].AsQueryable();
+ var q = new[] { 1 }.AsQueryable();
+
+ var a = q.FirstOrDefault();
+ var b = e.FirstOrDefault(44);
+
+ var c = q.FirstOrDefault(i => i == 0);
+ var d = q.FirstOrDefault(i => i == 0, 42);
+
+ var t = q.Take(1);
+ }
+
+ private static void Dynamic()
+ {
+ var e = new int[0].AsQueryable() as IQueryable;
+ var q = new[] { 1 }.AsQueryable() as IQueryable;
+
+ var a = q.FirstOrDefault();
+ //var b = e.FirstOrDefault(44);
+
+ var c = q.FirstOrDefault("it == 0");
+ //var d = q.FirstOrDefault(i => i == 0, 42);
+ }
+}
\ No newline at end of file
diff --git a/src/EntityFramework.DynamicLinq/EntityFramework.DynamicLinq.csproj b/src/EntityFramework.DynamicLinq/EntityFramework.DynamicLinq.csproj
index a0b95e83..518f2653 100644
--- a/src/EntityFramework.DynamicLinq/EntityFramework.DynamicLinq.csproj
+++ b/src/EntityFramework.DynamicLinq/EntityFramework.DynamicLinq.csproj
@@ -10,7 +10,7 @@
system;linq;dynamic;entityframework;core;async
{D3804228-91F4-4502-9595-39584E510000}
net45;net452;net46;netstandard2.1
- 1.6.$(PatchVersion)
+ 1.7.$(PatchVersion)
diff --git a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore10/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore10.csproj b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore10/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore10.csproj
new file mode 100644
index 00000000..54817a4c
--- /dev/null
+++ b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore10/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore10.csproj
@@ -0,0 +1,47 @@
+
+
+
+
+ Microsoft.EntityFrameworkCore.DynamicLinq
+ ../Microsoft.EntityFrameworkCore.DynamicLinq.EFCore2/Microsoft.EntityFrameworkCore.DynamicLinq.snk
+ Microsoft.EntityFrameworkCore.DynamicLinq
+ $(DefineConstants);EFCORE;EFCORE_3X;EFDYNAMICFUNCTIONS;ASYNCENUMERABLE
+ Dynamic Linq extensions for Microsoft.EntityFrameworkCore which adds Async support
+ system;linq;dynamic;entityframework;core;async
+ {1CD58B7F-CF5D-4F38-A5E0-9FE2D5216520}
+ net10.0
+ 10.7.$(PatchVersion)
+
+
+
+ full
+
+
+
+
+ portable
+ true
+
+
+
+ net10.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore10/Properties/AssemblyInfo.cs b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore10/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..69c2a3cd
--- /dev/null
+++ b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore10/Properties/AssemblyInfo.cs
@@ -0,0 +1,4 @@
+using System.Runtime.InteropServices;
+
+[assembly: ComVisible(false)]
+[assembly: Guid("b467c675-c014-4b55-85b9-9578941d2ef8")]
\ No newline at end of file
diff --git a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore2/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore2.csproj b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore2/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore2.csproj
index 859ac63e..2e9a5892 100644
--- a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore2/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore2.csproj
+++ b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore2/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore2.csproj
@@ -10,7 +10,7 @@
system;linq;dynamic;entityframework;core;async
{D3804228-91F4-4502-9595-39584E510001}
netstandard2.0
- 2.6.$(PatchVersion)
+ 2.7.$(PatchVersion)
diff --git a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore3/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore3.csproj b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore3/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore3.csproj
index f5dafeee..0254e9a8 100644
--- a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore3/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore3.csproj
+++ b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore3/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore3.csproj
@@ -10,7 +10,7 @@
system;linq;dynamic;entityframework;core;async
{7994FECC-965C-4A5D-8B0E-1A6EA769D4BE}
netstandard2.0
- 3.6.$(PatchVersion)
+ 3.7.$(PatchVersion)
diff --git a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore5/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore5.csproj b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore5/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore5.csproj
index 46c2f5f9..8393f083 100644
--- a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore5/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore5.csproj
+++ b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore5/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore5.csproj
@@ -10,7 +10,7 @@
system;linq;dynamic;entityframework;core;async
{D3804228-91F4-4502-9595-39584E519901}
netstandard2.1;net5.0
- 5.6.$(PatchVersion)
+ 5.7.$(PatchVersion)
diff --git a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore6/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore6.csproj b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore6/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore6.csproj
index 058a5abb..d6d07c0b 100644
--- a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore6/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore6.csproj
+++ b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore6/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore6.csproj
@@ -10,7 +10,7 @@
system;linq;dynamic;entityframework;core;async
{D28F6393-B56B-40A2-AF67-E8D669F42546}
net6.0
- 6.6.$(PatchVersion)
+ 6.7.$(PatchVersion)
diff --git a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore7/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore7.csproj b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore7/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore7.csproj
index 61eec0d8..0d590e5e 100644
--- a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore7/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore7.csproj
+++ b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore7/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore7.csproj
@@ -10,7 +10,7 @@
system;linq;dynamic;entityframework;core;async
{FB2F4C99-EC34-4D29-87E2-944B25D90ef7}
net6.0;net7.0
- 7.6.$(PatchVersion)
+ 7.7.$(PatchVersion)
diff --git a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore8/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore8.csproj b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore8/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore8.csproj
index 2e95aabf..b95d7eeb 100644
--- a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore8/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore8.csproj
+++ b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore8/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore8.csproj
@@ -10,7 +10,7 @@
system;linq;dynamic;entityframework;core;async
{9000129D-322D-4FE6-9C47-75464577C374}
net8.0
- 8.6.$(PatchVersion)
+ 8.7.$(PatchVersion)
diff --git a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore9/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore9.csproj b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore9/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore9.csproj
index ea16a63e..129a511b 100644
--- a/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore9/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore9.csproj
+++ b/src/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore9/Microsoft.EntityFrameworkCore.DynamicLinq.EFCore9.csproj
@@ -10,7 +10,7 @@
system;linq;dynamic;entityframework;core;async
{C774DAE7-54A0-4FCD-A3B7-3CB63D7E112D}
net9.0
- 9.6.$(PatchVersion)
+ 9.7.$(PatchVersion)
diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs
index fad7f9f1..3cb24306 100644
--- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs
+++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NewtonsoftJsonParsingConfig.cs
@@ -10,10 +10,28 @@ public class NewtonsoftJsonParsingConfig : ParsingConfig
///
/// The default ParsingConfig for .
///
- public new static NewtonsoftJsonParsingConfig Default { get; } = new();
+ public new static NewtonsoftJsonParsingConfig Default { get; } = new NewtonsoftJsonParsingConfig
+ {
+ ConvertObjectToSupportComparison = true
+ };
///
/// The default to use.
///
- public DynamicJsonClassOptions? DynamicJsonClassOptions { get; set; }
+ public DynamicJsonClassOptions? DynamicJsonClassOptions { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the objects in an array should be normalized before processing.
+ ///
+ public bool Normalize { get; set; } = true;
+
+ ///
+ /// Gets or sets the behavior to apply when a property value does not exist during normalization.
+ ///
+ ///
+ /// Use this property to control how the normalization process handles properties that are missing or undefined.
+ /// The selected behavior may affect the output or error handling of normalization operations.
+ /// The default value is .
+ ///
+ public NormalizationNonExistingPropertyBehavior NormalizationNonExistingPropertyValueBehavior { get; set; }
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NormalizationNonExistingPropertyBehavior.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NormalizationNonExistingPropertyBehavior.cs
new file mode 100644
index 00000000..44608e83
--- /dev/null
+++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Config/NormalizationNonExistingPropertyBehavior.cs
@@ -0,0 +1,17 @@
+namespace System.Linq.Dynamic.Core.NewtonsoftJson.Config;
+
+///
+/// Specifies the behavior to use when setting a property value that does not exist or is missing during normalization.
+///
+public enum NormalizationNonExistingPropertyBehavior
+{
+ ///
+ /// Specifies that a null value should be used.
+ ///
+ UseNull = 0,
+
+ ///
+ /// Specifies that the default value should be used.
+ ///
+ UseDefaultValue = 1
+}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs
index 1a7be2f4..2f79840b 100644
--- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs
+++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Extensions/JObjectExtensions.cs
@@ -44,10 +44,7 @@ private class JTokenResolvers : Dictionary func)
{
var array = new JArray();
- foreach (var dynamicElement in func())
+ var funcResult = func();
+ foreach (var dynamicElement in funcResult)
{
var element = dynamicElement switch
{
@@ -869,7 +871,16 @@ private static JArray ToJArray(Func func)
private static IQueryable ToQueryable(JArray source, NewtonsoftJsonParsingConfig? config = null)
{
- return source.ToDynamicJsonClassArray(config?.DynamicJsonClassOptions).AsQueryable();
+ config = config ?? NewtonsoftJsonParsingConfig.Default;
+ config.ConvertObjectToSupportComparison = true;
+
+ var normalized = config.Normalize == true ?
+ NormalizeUtils.NormalizeArray(source, config.NormalizationNonExistingPropertyValueBehavior) :
+ source;
+
+ return normalized
+ .ToDynamicJsonClassArray(config?.DynamicJsonClassOptions)
+ .AsQueryable();
}
#endregion
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj b/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj
index 4cbf4851..4d399df7 100644
--- a/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj
+++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/System.Linq.Dynamic.Core.NewtonsoftJson.csproj
@@ -8,8 +8,8 @@
Contains some extensions for System.Linq.Dynamic.Core to dynamically query a Newtonsoft.Json.JArray
system;linq;dynamic;core;dotnet;json
{8C5851B8-5C47-4229-AB55-D4252703598E}
- net45;net452;net46;netstandard2.0;netstandard2.1;net6.0;net8.0;net9.0
- 1.6.$(PatchVersion)
+ net45;net452;net46;netstandard2.0;netstandard2.1;net6.0;net8.0;net9.0;net10.0
+ 1.7.$(PatchVersion)
diff --git a/src/System.Linq.Dynamic.Core.NewtonsoftJson/Utils/NormalizeUtils.cs b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Utils/NormalizeUtils.cs
new file mode 100644
index 00000000..fe980319
--- /dev/null
+++ b/src/System.Linq.Dynamic.Core.NewtonsoftJson/Utils/NormalizeUtils.cs
@@ -0,0 +1,152 @@
+using System.Collections.Generic;
+using System.Linq.Dynamic.Core.NewtonsoftJson.Config;
+using Newtonsoft.Json.Linq;
+
+namespace System.Linq.Dynamic.Core.NewtonsoftJson.Utils;
+
+internal static class NormalizeUtils
+{
+ ///
+ /// Normalizes an array of JSON objects so that each object contains all properties found in the array,
+ /// including nested objects. Missing properties will have null values.
+ ///
+ internal static JArray NormalizeArray(JArray jsonArray, NormalizationNonExistingPropertyBehavior normalizationBehavior)
+ {
+ if (jsonArray.Any(item => item is not JObject))
+ {
+ return jsonArray;
+ }
+
+ var schema = BuildSchema(jsonArray);
+ var normalizedArray = new JArray();
+
+ foreach (var jo in jsonArray.OfType())
+ {
+ var normalizedObj = NormalizeObject(jo, schema, normalizationBehavior);
+ normalizedArray.Add(normalizedObj);
+ }
+
+ return normalizedArray;
+ }
+
+ private static Dictionary BuildSchema(JArray array)
+ {
+ var schema = new Dictionary();
+
+ foreach (var item in array)
+ {
+ if (item is JObject obj)
+ {
+ MergeSchema(schema, obj);
+ }
+ }
+
+ return schema;
+ }
+
+ private static void MergeSchema(Dictionary schema, JObject obj)
+ {
+ foreach (var prop in obj.Properties())
+ {
+ if (prop.Value is JObject nested)
+ {
+ if (!schema.TryGetValue(prop.Name, out var jsonValueInfo))
+ {
+ jsonValueInfo = new JsonValueInfo(JTokenType.Object, new Dictionary());
+ schema[prop.Name] = jsonValueInfo;
+ }
+
+ MergeSchema((Dictionary)jsonValueInfo.Value!, nested);
+ }
+ else
+ {
+ if (!schema.ContainsKey(prop.Name))
+ {
+ schema[prop.Name] = new JsonValueInfo(prop.Value.Type, null);
+ }
+ }
+ }
+ }
+
+ private static JObject NormalizeObject(JObject source, Dictionary schema, NormalizationNonExistingPropertyBehavior normalizationBehavior)
+ {
+ var result = new JObject();
+
+ foreach (var key in schema.Keys)
+ {
+ if (schema[key].Value is Dictionary nestedSchema)
+ {
+ result[key] = source.ContainsKey(key) && source[key] is JObject jo ? NormalizeObject(jo, nestedSchema, normalizationBehavior) : CreateEmptyObject(nestedSchema, normalizationBehavior);
+ }
+ else
+ {
+ if (source.ContainsKey(key))
+ {
+ result[key] = source[key];
+ }
+ else
+ {
+ result[key] = GetDefaultOrNullValue(normalizationBehavior, schema[key]);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private static JObject CreateEmptyObject(Dictionary schema, NormalizationNonExistingPropertyBehavior normalizationBehavior)
+ {
+ var obj = new JObject();
+ foreach (var key in schema.Keys)
+ {
+ if (schema[key].Value is Dictionary nestedSchema)
+ {
+ obj[key] = CreateEmptyObject(nestedSchema, normalizationBehavior);
+ }
+ else
+ {
+ obj[key] = GetDefaultOrNullValue(normalizationBehavior, schema[key]);
+ }
+ }
+
+ return obj;
+ }
+
+ private static JToken GetDefaultValue(JsonValueInfo jType)
+ {
+ return jType.Type switch
+ {
+ JTokenType.Array => new JArray(),
+ JTokenType.Boolean => default(bool),
+ JTokenType.Bytes => new byte[0],
+ JTokenType.Date => DateTime.MinValue,
+ JTokenType.Float => default(float),
+ JTokenType.Guid => Guid.Empty,
+ JTokenType.Integer => default(int),
+ JTokenType.String => string.Empty,
+ JTokenType.TimeSpan => TimeSpan.MinValue,
+ _ => GetNullValue(jType),
+ };
+ }
+
+ private static JValue GetNullValue(JsonValueInfo jType)
+ {
+ return jType.Type switch
+ {
+ JTokenType.Boolean => new JValue((bool?)null),
+ JTokenType.Bytes => new JValue((byte[]?)null),
+ JTokenType.Date => new JValue((DateTime?)null),
+ JTokenType.Float => new JValue((float?)null),
+ JTokenType.Guid => new JValue((Guid?)null),
+ JTokenType.Integer => new JValue((int?)null),
+ JTokenType.String => new JValue((string?)null),
+ JTokenType.TimeSpan => new JValue((TimeSpan?)null),
+ _ => JValue.CreateNull(),
+ };
+ }
+
+ private static JToken GetDefaultOrNullValue(NormalizationNonExistingPropertyBehavior behavior, JsonValueInfo jType)
+ {
+ return behavior == NormalizationNonExistingPropertyBehavior.UseDefaultValue ? GetDefaultValue(jType) : GetNullValue(jType);
+ }
+}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/NormalizationNonExistingPropertyBehavior.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/NormalizationNonExistingPropertyBehavior.cs
new file mode 100644
index 00000000..381f0408
--- /dev/null
+++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/NormalizationNonExistingPropertyBehavior.cs
@@ -0,0 +1,17 @@
+namespace System.Linq.Dynamic.Core.SystemTextJson.Config;
+
+///
+/// Specifies the behavior to use when setting a property value that does not exist or is missing during normalization.
+///
+public enum NormalizationNonExistingPropertyBehavior
+{
+ ///
+ /// Specifies that a null value should be used.
+ ///
+ UseNull = 0,
+
+ ///
+ /// Specifies that the default value should be used.
+ ///
+ UseDefaultValue = 1
+}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs
index eb42ff6d..4892bcf2 100644
--- a/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs
+++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Config/SystemTextJsonParsingConfig.cs
@@ -8,5 +8,23 @@ public class SystemTextJsonParsingConfig : ParsingConfig
///
/// The default ParsingConfig for .
///
- public new static SystemTextJsonParsingConfig Default { get; } = new();
+ public new static SystemTextJsonParsingConfig Default { get; } = new SystemTextJsonParsingConfig
+ {
+ ConvertObjectToSupportComparison = true
+ };
+
+ ///
+ /// Gets or sets a value indicating whether the objects in an array should be normalized before processing.
+ ///
+ public bool Normalize { get; set; } = true;
+
+ ///
+ /// Gets or sets the behavior to apply when a property value does not exist during normalization.
+ ///
+ ///
+ /// Use this property to control how the normalization process handles properties that are missing or undefined.
+ /// The selected behavior may affect the output or error handling of normalization operations.
+ /// The default value is .
+ ///
+ public NormalizationNonExistingPropertyBehavior NormalizationNonExistingPropertyValueBehavior { get; set; }
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs
index 7daf15a5..3437d3c9 100644
--- a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs
+++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonDocumentExtensions.cs
@@ -34,10 +34,7 @@ private class JTokenResolvers : Dictionary src, Type newType)
{
var method = ConvertToTypedArrayGenericMethod.MakeGenericMethod(newType);
- return (IEnumerable)method.Invoke(null, new object[] { src })!;
+ return (IEnumerable)method.Invoke(null, [src])!;
}
private static readonly MethodInfo ConvertToTypedArrayGenericMethod = typeof(JsonDocumentExtensions).GetMethod(nameof(ConvertToTypedArrayGeneric), BindingFlags.NonPublic | BindingFlags.Static)!;
diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonValueExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonValueExtensions.cs
new file mode 100644
index 00000000..907f7bf3
--- /dev/null
+++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Extensions/JsonValueExtensions.cs
@@ -0,0 +1,21 @@
+#if !NET8_0_OR_GREATER
+namespace System.Text.Json.Nodes;
+
+internal static class JsonValueExtensions
+{
+ internal static JsonValueKind? GetValueKind(this JsonNode node)
+ {
+ if (node is JsonObject)
+ {
+ return JsonValueKind.Object;
+ }
+
+ if (node is JsonArray)
+ {
+ return JsonValueKind.Array;
+ }
+
+ return node.GetValue
diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs
index 8d1671a0..6d8a5aa0 100644
--- a/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs
+++ b/src/System.Linq.Dynamic.Core.SystemTextJson/SystemTextJsonExtensions.cs
@@ -1093,13 +1093,20 @@ private static JsonDocument ToJsonDocumentArray(Func func)
// ReSharper disable once UnusedParameter.Local
private static IQueryable ToQueryable(JsonDocument source, SystemTextJsonParsingConfig? config = null)
{
+ config = config ?? SystemTextJsonParsingConfig.Default;
+ config.ConvertObjectToSupportComparison = true;
+
var array = source.RootElement;
if (array.ValueKind != JsonValueKind.Array)
{
throw new NotSupportedException("The source is not a JSON array.");
}
- return JsonDocumentExtensions.ToDynamicJsonClassArray(array).AsQueryable();
+ var normalized = config.Normalize ?
+ NormalizeUtils.NormalizeJsonDocument(source, config.NormalizationNonExistingPropertyValueBehavior) :
+ source;
+
+ return JsonDocumentExtensions.ToDynamicJsonClassArray(normalized.RootElement).AsQueryable();
}
#endregion
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core.SystemTextJson/Utils/NormalizeUtils.cs b/src/System.Linq.Dynamic.Core.SystemTextJson/Utils/NormalizeUtils.cs
new file mode 100644
index 00000000..7f6e4abf
--- /dev/null
+++ b/src/System.Linq.Dynamic.Core.SystemTextJson/Utils/NormalizeUtils.cs
@@ -0,0 +1,175 @@
+using System.Collections.Generic;
+using System.Linq.Dynamic.Core.SystemTextJson.Config;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+
+namespace System.Linq.Dynamic.Core.SystemTextJson.Utils;
+
+internal static class NormalizeUtils
+{
+ ///
+ /// Normalizes a document so that each object contains all properties found in the array, including nested objects.
+ ///
+ internal static JsonDocument NormalizeJsonDocument(JsonDocument jsonDocument, NormalizationNonExistingPropertyBehavior normalizationBehavior)
+ {
+ if (jsonDocument.RootElement.ValueKind != JsonValueKind.Array)
+ {
+ throw new NotSupportedException("The source is not a JSON array.");
+ }
+
+ var jsonArray = JsonNode.Parse(jsonDocument.RootElement.GetRawText())!.AsArray();
+ var normalizedArray = NormalizeJsonArray(jsonArray, normalizationBehavior);
+
+ return JsonDocument.Parse(normalizedArray.ToJsonString());
+ }
+
+ ///
+ /// Normalizes an array of JSON objects so that each object contains all properties found in the array, including nested objects.
+ ///
+ internal static JsonArray NormalizeJsonArray(JsonArray jsonArray, NormalizationNonExistingPropertyBehavior normalizationBehavior)
+ {
+ if (jsonArray.Any(item => item != null && item.GetValueKind() != JsonValueKind.Object))
+ {
+ return jsonArray;
+ }
+
+ var schema = BuildSchema(jsonArray);
+ var normalizedArray = new JsonArray();
+
+ foreach (var item in jsonArray)
+ {
+ if (item is JsonObject obj)
+ {
+ var normalizedObj = NormalizeObject(obj, schema, normalizationBehavior);
+ normalizedArray.Add(normalizedObj);
+ }
+ }
+
+ return normalizedArray;
+ }
+
+ private static Dictionary BuildSchema(JsonArray array)
+ {
+ var schema = new Dictionary();
+
+ foreach (var item in array)
+ {
+ if (item is JsonObject obj)
+ {
+ MergeSchema(schema, obj);
+ }
+ }
+
+ return schema;
+ }
+
+ private static void MergeSchema(Dictionary schema, JsonObject obj)
+ {
+ foreach (var prop in obj)
+ {
+ if (prop.Value is JsonObject nested)
+ {
+ if (!schema.TryGetValue(prop.Key, out var jsonValueInfo))
+ {
+ jsonValueInfo = new JsonValueInfo(JsonValueKind.Object, new Dictionary());
+ schema[prop.Key] = jsonValueInfo;
+ }
+
+ MergeSchema((Dictionary)jsonValueInfo.Value!, nested);
+ }
+ else
+ {
+ if (!schema.ContainsKey(prop.Key))
+ {
+ schema[prop.Key] = new JsonValueInfo(prop.Value?.GetValueKind() ?? JsonValueKind.Null, null);
+ }
+ }
+ }
+ }
+
+ private static JsonObject NormalizeObject(JsonObject source, Dictionary schema, NormalizationNonExistingPropertyBehavior normalizationBehavior)
+ {
+ var result = new JsonObject();
+
+ foreach (var kvp in schema)
+ {
+ var key = kvp.Key;
+ var jType = kvp.Value;
+
+ if (jType.Value is Dictionary nestedSchema)
+ {
+ result[key] = source.ContainsKey(key) && source[key] is JsonObject jo ? NormalizeObject(jo, nestedSchema, normalizationBehavior) : CreateEmptyObject(nestedSchema, normalizationBehavior);
+ }
+ else
+ {
+ if (source.ContainsKey(key))
+ {
+ var value = source[key];
+#if NET8_0_OR_GREATER
+ result[key] = value?.DeepClone();
+#else
+ result[key] = value != null ? JsonNode.Parse(value.ToJsonString()) : null;
+#endif
+ }
+ else
+ {
+ result[key] = GetDefaultOrNullValue(normalizationBehavior, jType);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private static JsonObject CreateEmptyObject(Dictionary schema, NormalizationNonExistingPropertyBehavior normalizationBehavior)
+ {
+ var obj = new JsonObject();
+ foreach (var kvp in schema)
+ {
+ var key = kvp.Key;
+ var jType = kvp.Value;
+
+ if (jType.Value is Dictionary nestedSchema)
+ {
+ obj[key] = CreateEmptyObject(nestedSchema, normalizationBehavior);
+ }
+ else
+ {
+ obj[key] = GetDefaultOrNullValue(normalizationBehavior, jType);
+ }
+ }
+
+ return obj;
+ }
+
+ private static JsonNode? GetDefaultValue(JsonValueInfo jType)
+ {
+ return jType.Type switch
+ {
+ JsonValueKind.Array => new JsonArray(),
+ JsonValueKind.False => false,
+ JsonValueKind.Number => default(int),
+ JsonValueKind.String => string.Empty,
+ JsonValueKind.True => false,
+ _ => GetNullValue(jType),
+ };
+ }
+
+ private static JsonNode? GetNullValue(JsonValueInfo jType)
+ {
+ return jType.Type switch
+ {
+ JsonValueKind.Array => null,
+ JsonValueKind.False => JsonValue.Create(false),
+ JsonValueKind.Number => JsonValue.Create(null),
+ JsonValueKind.String => JsonValue.Create(null),
+ JsonValueKind.True => JsonValue.Create(true),
+ _ => null,
+ };
+ }
+
+ private static JsonNode? GetDefaultOrNullValue(NormalizationNonExistingPropertyBehavior behavior, JsonValueInfo jType)
+ {
+ return behavior == NormalizationNonExistingPropertyBehavior.UseDefaultValue ? GetDefaultValue(jType) : GetNullValue(jType);
+ }
+}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/DynamicClass.cs b/src/System.Linq.Dynamic.Core/DynamicClass.cs
index 33f06aee..64685f5e 100644
--- a/src/System.Linq.Dynamic.Core/DynamicClass.cs
+++ b/src/System.Linq.Dynamic.Core/DynamicClass.cs
@@ -123,6 +123,16 @@ public object? this[string name]
}
}
+ ///
+ /// Determines whether a property with the specified name exists in the collection.
+ ///
+ /// The name of the property to locate. Cannot be null.
+ /// true if a property with the specified name exists; otherwise, false.
+ public bool ContainsProperty(string name)
+ {
+ return Properties.ContainsKey(name);
+ }
+
///
/// Returns the enumeration of all dynamic member names.
///
diff --git a/src/System.Linq.Dynamic.Core/DynamicClass.uap.cs b/src/System.Linq.Dynamic.Core/DynamicClass.uap.cs
index 8778de98..cf28afd3 100644
--- a/src/System.Linq.Dynamic.Core/DynamicClass.uap.cs
+++ b/src/System.Linq.Dynamic.Core/DynamicClass.uap.cs
@@ -12,7 +12,7 @@ public class DynamicClass : DynamicObject
{
internal const string IndexerName = "System_Linq_Dynamic_Core_DynamicClass_Indexer";
- private readonly Dictionary _properties = new();
+ private readonly Dictionary _properties = new();
///
/// Initializes a new instance of the class.
@@ -35,7 +35,7 @@ public DynamicClass(params KeyValuePair[] propertylist)
/// The name.
/// Value from the property.
[IndexerName(IndexerName)]
- public object this[string name]
+ public object? this[string name]
{
get
{
@@ -59,6 +59,16 @@ public object this[string name]
}
}
+ ///
+ /// Determines whether a property with the specified name exists in the collection.
+ ///
+ /// The name of the property to locate. Cannot be null.
+ /// true if a property with the specified name exists; otherwise, false.
+ public bool ContainsProperty(string name)
+ {
+ return _properties.ContainsKey(name);
+ }
+
///
/// Returns the enumeration of all dynamic member names.
///
diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs
index 7d72ad61..8222d4d7 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs
@@ -11,6 +11,7 @@ namespace System.Linq.Dynamic.Core.Parser;
internal class ExpressionHelper : IExpressionHelper
{
+ private static readonly Expression _nullExpression = Expression.Constant(null);
private readonly IConstantExpressionWrapper _constantExpressionWrapper = new ConstantExpressionWrapper();
private readonly ParsingConfig _parsingConfig;
@@ -340,7 +341,7 @@ public bool TryGenerateAndAlsoNotNullExpression(Expression sourceExpression, boo
// Convert all expressions into '!= null' expressions (only if the type can be null)
var binaryExpressions = expressions
.Where(expression => TypeHelper.TypeCanBeNull(expression.Type))
- .Select(expression => Expression.NotEqual(expression, Expression.Constant(null)))
+ .Select(expression => Expression.NotEqual(expression, _nullExpression))
.ToArray();
// Convert all binary expressions into `AndAlso(...)`
@@ -361,7 +362,12 @@ public bool ExpressionQualifiesForNullPropagation(Expression? expression)
public Expression GenerateDefaultExpression(Type type)
{
#if NET35
- return Expression.Constant(Activator.CreateInstance(type));
+ if (type.IsValueType)
+ {
+ return Expression.Constant(Activator.CreateInstance(type), type);
+ }
+
+ return Expression.Constant(null, type);
#else
return Expression.Default(type);
#endif
@@ -388,11 +394,49 @@ public bool TryConvertTypes(ref Expression left, ref Expression right)
if (left.Type == typeof(object))
{
- left = Expression.Convert(left, right.Type);
+ if (TryGetAsIndexerExpression(left, out var ce))
+ {
+ var rightTypeAsNullableType = TypeHelper.GetNullableType(right.Type);
+
+ right = Expression.Convert(right, rightTypeAsNullableType);
+
+ left = Expression.Condition(
+ ce.Test,
+ Expression.Convert(ce.IfTrue, rightTypeAsNullableType),
+ Expression.Convert(_nullExpression, rightTypeAsNullableType)
+ );
+
+ return true;
+ }
+
+ left = Expression.Condition(
+ Expression.Equal(left, _nullExpression),
+ GenerateDefaultExpression(right.Type),
+ Expression.Convert(left, right.Type)
+ );
}
else if (right.Type == typeof(object))
{
- right = Expression.Convert(right, left.Type);
+ if (TryGetAsIndexerExpression(right, out var ce))
+ {
+ var leftTypeAsNullableType = TypeHelper.GetNullableType(left.Type);
+
+ left = Expression.Convert(left, leftTypeAsNullableType);
+
+ right = Expression.Condition(
+ ce.Test,
+ Expression.Convert(ce.IfTrue, leftTypeAsNullableType),
+ Expression.Convert(_nullExpression, leftTypeAsNullableType)
+ );
+
+ return true;
+ }
+
+ right = Expression.Condition(
+ Expression.Equal(right, _nullExpression),
+ GenerateDefaultExpression(left.Type),
+ Expression.Convert(right, left.Type)
+ );
}
return true;
@@ -533,4 +577,17 @@ private static object[] ConvertIfIEnumerableHasValues(IEnumerable? input)
return [];
}
+
+ private static bool TryGetAsIndexerExpression(Expression expression, [NotNullWhen(true)] out ConditionalExpression? indexerExpresion)
+ {
+ indexerExpresion = expression as ConditionalExpression;
+ if (indexerExpresion == null)
+ {
+ return false;
+ }
+
+ return
+ indexerExpresion.IfTrue.ToString().Contains(DynamicClass.IndexerName) &&
+ indexerExpresion.Test.ToString().Contains("ContainsProperty");
+ }
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
index 4cc77a00..b8091f55 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
@@ -984,11 +984,12 @@ private Expression ParseRealLiteral()
{
_textParser.ValidateToken(TokenId.RealLiteral);
- string text = _textParser.CurrentToken.Text;
+ var text = _textParser.CurrentToken.Text;
+ var textOriginal = text;
_textParser.NextToken();
- return _numberParser.ParseRealLiteral(text, text[text.Length - 1], true);
+ return _numberParser.ParseRealLiteral(text, textOriginal, text[text.Length - 1], true);
}
private Expression ParseParenExpression()
@@ -1920,11 +1921,35 @@ private Expression ParseMemberAccess(Type? type, Expression? expression, string?
if (!_parsingConfig.DisableMemberAccessToIndexAccessorFallback && extraCheck)
{
- var indexerName = TypeHelper.IsDynamicClass(type!) ? DynamicClass.IndexerName : "Item";
+ var isDynamicClass = TypeHelper.IsDynamicClass(type!);
+ var indexerName = isDynamicClass ? DynamicClass.IndexerName : "Item";
+
+ // Try to get the indexer property "Item" or "DynamicClass_Indexer" which takes a string as parameter
var indexerMethod = expression?.Type.GetMethod($"get_{indexerName}", [typeof(string)]);
if (indexerMethod != null)
{
- return Expression.Call(expression, indexerMethod, Expression.Constant(id));
+ if (!isDynamicClass)
+ {
+ return Expression.Call(expression, indexerMethod, Expression.Constant(id));
+ }
+
+ var containsPropertyMethod = typeof(DynamicClass).GetMethod("ContainsProperty");
+ if (containsPropertyMethod == null)
+ {
+ return Expression.Call(expression, indexerMethod, Expression.Constant(id));
+ }
+
+ var callContainsPropertyExpression = Expression.Call(
+ expression!,
+ containsPropertyMethod,
+ Expression.Constant(id)
+ );
+
+ return Expression.Condition(
+ Expression.Equal(callContainsPropertyExpression, Expression.Constant(true)),
+ Expression.Call(expression, indexerMethod, Expression.Constant(id)),
+ Expression.Constant(null)
+ );
}
}
diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs
index 088b755e..49731b24 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs
@@ -122,7 +122,7 @@ public ExpressionPromoter(ParsingConfig config)
if (TypeHelper.IsCompatibleWith(returnType, type))
{
- if (type == typeof(decimal) && TypeHelper.IsEnumType(sourceExpression.Type))
+ if (TypeHelper.TypesAreEqual(type, typeof(decimal)) && TypeHelper.IsEnumType(sourceExpression.Type))
{
return Expression.Convert(Expression.Convert(sourceExpression, Enum.GetUnderlyingType(sourceExpression.Type)), type);
}
diff --git a/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs b/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs
index bbc691cd..4e52949b 100644
--- a/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/IExpressionHelper.cs
@@ -52,5 +52,5 @@ internal interface IExpressionHelper
///
/// If the types are different (and not null), try to convert the object type to other type.
///
- public bool TryConvertTypes(ref Expression left, ref Expression right);
+ bool TryConvertTypes(ref Expression left, ref Expression right);
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs b/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs
index 7fdfdaad..f4bbc731 100644
--- a/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs
@@ -4,244 +4,234 @@
using System.Linq.Expressions;
using System.Text.RegularExpressions;
-namespace System.Linq.Dynamic.Core.Parser
+namespace System.Linq.Dynamic.Core.Parser;
+
+///
+/// NumberParser
+///
+public class NumberParser
{
+ private static readonly Regex RegexBinary32 = new("^[01]{1,32}$", RegexOptions.Compiled);
+ private static readonly Regex RegexBinary64 = new("^[01]{1,64}$", RegexOptions.Compiled);
+ private static readonly char[] Qualifiers = { 'U', 'u', 'L', 'l', 'F', 'f', 'D', 'd', 'M', 'm' };
+ private static readonly char[] QualifiersHex = { 'U', 'u', 'L', 'l' };
+ private static readonly string[] QualifiersReal = { "F", "f", "D", "d", "M", "m" };
+ private readonly ConstantExpressionHelper _constantExpressionHelper;
+
+ private readonly CultureInfo _culture;
+
///
- /// NumberParser
+ /// Initializes a new instance of the class.
///
- public class NumberParser
+ /// The ParsingConfig.
+ public NumberParser(ParsingConfig? config)
{
- private static readonly Regex RegexBinary32 = new("^[01]{1,32}$", RegexOptions.Compiled);
- private static readonly Regex RegexBinary64 = new("^[01]{1,64}$", RegexOptions.Compiled);
- private static readonly char[] Qualifiers = { 'U', 'u', 'L', 'l', 'F', 'f', 'D', 'd', 'M', 'm' };
- private static readonly char[] QualifiersHex = { 'U', 'u', 'L', 'l' };
- private static readonly string[] QualifiersReal = { "F", "f", "D", "d", "M", "m" };
- private readonly ConstantExpressionHelper _constantExpressionHelper;
-
- private readonly CultureInfo _culture;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The ParsingConfig.
- public NumberParser(ParsingConfig? config)
- {
- _culture = config?.NumberParseCulture ?? CultureInfo.InvariantCulture;
- _constantExpressionHelper = ConstantExpressionHelperFactory.GetInstance(config ?? ParsingConfig.Default);
- }
+ _culture = config?.NumberParseCulture ?? CultureInfo.InvariantCulture;
+ _constantExpressionHelper = ConstantExpressionHelperFactory.GetInstance(config ?? ParsingConfig.Default);
+ }
- ///
- /// Tries to parse the text into a IntegerLiteral ConstantExpression.
- ///
- /// The current token position (needed for error reporting).
- /// The text.
- public Expression ParseIntegerLiteral(int tokenPosition, string text)
- {
- Check.NotEmpty(text, nameof(text));
+ ///
+ /// Tries to parse the text into a IntegerLiteral ConstantExpression.
+ ///
+ /// The current token position (needed for error reporting).
+ /// The text.
+ public Expression ParseIntegerLiteral(int tokenPosition, string text)
+ {
+ Check.NotEmpty(text);
- var last = text[text.Length - 1];
- var isNegative = text[0] == '-';
- var isHexadecimal = text.StartsWith(isNegative ? "-0x" : "0x", StringComparison.OrdinalIgnoreCase);
- var isBinary = text.StartsWith(isNegative ? "-0b" : "0b", StringComparison.OrdinalIgnoreCase);
- var qualifiers = isHexadecimal ? QualifiersHex : Qualifiers;
+ var textOriginal = text;
+ var last = text[text.Length - 1];
+ var isNegative = text[0] == '-';
+ var isHexadecimal = text.StartsWith(isNegative ? "-0x" : "0x", StringComparison.OrdinalIgnoreCase);
+ var isBinary = text.StartsWith(isNegative ? "-0b" : "0b", StringComparison.OrdinalIgnoreCase);
+ var qualifiers = isHexadecimal ? QualifiersHex : Qualifiers;
- string? qualifier = null;
- if (qualifiers.Contains(last))
+ string? qualifier = null;
+ if (qualifiers.Contains(last))
+ {
+ int pos = text.Length - 1, count = 0;
+ while (qualifiers.Contains(text[pos]))
{
- int pos = text.Length - 1, count = 0;
- while (qualifiers.Contains(text[pos]))
- {
- ++count;
- --pos;
- }
- qualifier = text.Substring(text.Length - count, count);
- text = text.Substring(0, text.Length - count);
+ ++count;
+ --pos;
}
+ qualifier = text.Substring(text.Length - count, count);
+ text = text.Substring(0, text.Length - count);
+ }
- if (!isNegative)
+ if (!isNegative)
+ {
+ if (isHexadecimal || isBinary)
{
- if (isHexadecimal || isBinary)
- {
- text = text.Substring(2);
- }
-
- if (isBinary)
- {
- return ParseAsBinary(tokenPosition, text, isNegative);
- }
+ text = text.Substring(2);
+ }
- if (!ulong.TryParse(text, isHexadecimal ? NumberStyles.HexNumber : NumberStyles.Integer, _culture, out ulong unsignedValue))
- {
- throw new ParseException(string.Format(_culture, Res.InvalidIntegerLiteral, text), tokenPosition);
- }
+ if (isBinary)
+ {
+ return ParseAsBinary(tokenPosition, text, textOriginal, isNegative);
+ }
- if (!string.IsNullOrEmpty(qualifier) && qualifier!.Length > 0)
- {
- if (qualifier == "U" || qualifier == "u")
- {
- return _constantExpressionHelper.CreateLiteral((uint)unsignedValue, text);
- }
-
- if (qualifier == "L" || qualifier == "l")
- {
- return _constantExpressionHelper.CreateLiteral((long)unsignedValue, text);
- }
-
- if (QualifiersReal.Contains(qualifier))
- {
- return ParseRealLiteral(text, qualifier[0], false);
- }
-
- return _constantExpressionHelper.CreateLiteral(unsignedValue, text);
- }
+ if (!ulong.TryParse(text, isHexadecimal ? NumberStyles.HexNumber : NumberStyles.Integer, _culture, out ulong unsignedValue))
+ {
+ throw new ParseException(string.Format(_culture, Res.InvalidIntegerLiteral, text), tokenPosition);
+ }
- if (unsignedValue <= int.MaxValue)
+ if (!string.IsNullOrEmpty(qualifier) && qualifier!.Length > 0)
+ {
+ if (qualifier == "U" || qualifier == "u")
{
- return _constantExpressionHelper.CreateLiteral((int)unsignedValue, text);
+ return _constantExpressionHelper.CreateLiteral((uint)unsignedValue, textOriginal);
}
- if (unsignedValue <= uint.MaxValue)
+ if (qualifier == "L" || qualifier == "l")
{
- return _constantExpressionHelper.CreateLiteral((uint)unsignedValue, text);
+ return _constantExpressionHelper.CreateLiteral((long)unsignedValue, textOriginal);
}
- if (unsignedValue <= long.MaxValue)
+ if (QualifiersReal.Contains(qualifier))
{
- return _constantExpressionHelper.CreateLiteral((long)unsignedValue, text);
+ return ParseRealLiteral(text, textOriginal, qualifier[0], false);
}
- return _constantExpressionHelper.CreateLiteral(unsignedValue, text);
+ return _constantExpressionHelper.CreateLiteral(unsignedValue, textOriginal);
}
- if (isHexadecimal || isBinary)
+ if (unsignedValue <= int.MaxValue)
{
- text = text.Substring(3);
+ return _constantExpressionHelper.CreateLiteral((int)unsignedValue, textOriginal);
}
- if (isBinary)
+ if (unsignedValue <= uint.MaxValue)
{
- return ParseAsBinary(tokenPosition, text, isNegative);
+ return _constantExpressionHelper.CreateLiteral((uint)unsignedValue, textOriginal);
}
- if (!long.TryParse(text, isHexadecimal ? NumberStyles.HexNumber : NumberStyles.Integer, _culture, out long value))
+ if (unsignedValue <= long.MaxValue)
{
- throw new ParseException(string.Format(_culture, Res.InvalidIntegerLiteral, text), tokenPosition);
+ return _constantExpressionHelper.CreateLiteral((long)unsignedValue, textOriginal);
}
- if (isHexadecimal)
- {
- value = -value;
- }
+ return _constantExpressionHelper.CreateLiteral(unsignedValue, textOriginal);
+ }
- if (!string.IsNullOrEmpty(qualifier) && qualifier!.Length > 0)
- {
- if (qualifier == "L" || qualifier == "l")
- {
- return _constantExpressionHelper.CreateLiteral(value, text);
- }
+ if (isHexadecimal || isBinary)
+ {
+ text = text.Substring(3);
+ }
- if (QualifiersReal.Contains(qualifier))
- {
- return ParseRealLiteral(text, qualifier[0], false);
- }
+ if (isBinary)
+ {
+ return ParseAsBinary(tokenPosition, text, textOriginal, isNegative);
+ }
- throw new ParseException(Res.MinusCannotBeAppliedToUnsignedInteger, tokenPosition);
+ if (!long.TryParse(text, isHexadecimal ? NumberStyles.HexNumber : NumberStyles.Integer, _culture, out long value))
+ {
+ throw new ParseException(string.Format(_culture, Res.InvalidIntegerLiteral, text), tokenPosition);
+ }
+
+ if (isHexadecimal)
+ {
+ value = -value;
+ }
+
+ if (!string.IsNullOrEmpty(qualifier) && qualifier!.Length > 0)
+ {
+ if (qualifier == "L" || qualifier == "l")
+ {
+ return _constantExpressionHelper.CreateLiteral(value, textOriginal);
}
- if (value <= int.MaxValue)
+ if (QualifiersReal.Contains(qualifier))
{
- return _constantExpressionHelper.CreateLiteral((int)value, text);
+ return ParseRealLiteral(text, textOriginal, qualifier[0], false);
}
- return _constantExpressionHelper.CreateLiteral(value, text);
+ throw new ParseException(Res.MinusCannotBeAppliedToUnsignedInteger, tokenPosition);
}
- ///
- /// Parse the text into a Real ConstantExpression.
- ///
- public Expression ParseRealLiteral(string text, char qualifier, bool stripQualifier)
+ if (value >= int.MinValue && value <= int.MaxValue)
{
- if (stripQualifier)
- {
- var pos = text.Length - 1;
- while (pos >= 0 && Qualifiers.Contains(text[pos]))
- {
- pos--;
- }
+ return _constantExpressionHelper.CreateLiteral((int)value, textOriginal);
+ }
- if (pos < text.Length - 1)
- {
- qualifier = text[pos + 1];
- text = text.Substring(0, pos + 1);
- }
- }
+ return _constantExpressionHelper.CreateLiteral(value, textOriginal);
+ }
- switch (qualifier)
+ ///
+ /// Parse the text into a Real ConstantExpression.
+ ///
+ public Expression ParseRealLiteral(string text, string textOriginal, char qualifier, bool stripQualifier)
+ {
+ if (stripQualifier)
+ {
+ var pos = text.Length - 1;
+ while (pos >= 0 && Qualifiers.Contains(text[pos]))
{
- case 'f':
- case 'F':
- return _constantExpressionHelper.CreateLiteral(ParseNumber(text, typeof(float))!, text);
-
- case 'm':
- case 'M':
- return _constantExpressionHelper.CreateLiteral(ParseNumber(text, typeof(decimal))!, text);
-
- case 'd':
- case 'D':
- return _constantExpressionHelper.CreateLiteral(ParseNumber(text, typeof(double))!, text);
+ pos--;
+ }
- default:
- return _constantExpressionHelper.CreateLiteral(ParseNumber(text, typeof(double))!, text);
+ if (pos < text.Length - 1)
+ {
+ qualifier = text[pos + 1];
+ text = text.Substring(0, pos + 1);
}
}
- ///
- /// Tries to parse the number (text) into the specified type.
- ///
- /// The text.
- /// The type.
- /// The result.
- public bool TryParseNumber(string text, Type type, out object? result)
+ return qualifier switch
{
- result = ParseNumber(text, type);
- return result != null;
- }
+ 'f' or 'F' => _constantExpressionHelper.CreateLiteral(ParseNumber(text, typeof(float))!, textOriginal),
+ 'm' or 'M' => _constantExpressionHelper.CreateLiteral(ParseNumber(text, typeof(decimal))!, textOriginal),
+ _ => _constantExpressionHelper.CreateLiteral(ParseNumber(text, typeof(double))!, textOriginal)
+ };
+ }
- ///
- /// Parses the number (text) into the specified type.
- ///
- /// The text.
- /// The type.
- public object? ParseNumber(string text, Type type)
+ ///
+ /// Tries to parse the number (text) into the specified type.
+ ///
+ /// The text.
+ /// The type.
+ /// The result.
+ public bool TryParseNumber(string text, Type type, out object? result)
+ {
+ result = ParseNumber(text, type);
+ return result != null;
+ }
+
+ ///
+ /// Parses the number (text) into the specified type.
+ ///
+ /// The text.
+ /// The type.
+ public object? ParseNumber(string text, Type type)
+ {
+ try
{
- try
- {
#if !(UAP10_0 || NETSTANDARD)
- switch (Type.GetTypeCode(TypeHelper.GetNonNullableType(type)))
- {
- case TypeCode.SByte:
- return sbyte.Parse(text, _culture);
- case TypeCode.Byte:
- return byte.Parse(text, _culture);
- case TypeCode.Int16:
- return short.Parse(text, _culture);
- case TypeCode.UInt16:
- return ushort.Parse(text, _culture);
- case TypeCode.Int32:
- return int.Parse(text, _culture);
- case TypeCode.UInt32:
- return uint.Parse(text, _culture);
- case TypeCode.Int64:
- return long.Parse(text, _culture);
- case TypeCode.UInt64:
- return ulong.Parse(text, _culture);
- case TypeCode.Single:
- return float.Parse(text, _culture);
- case TypeCode.Double:
- return double.Parse(text, _culture);
- case TypeCode.Decimal:
- return decimal.Parse(text, _culture);
- }
+ switch (Type.GetTypeCode(TypeHelper.GetNonNullableType(type)))
+ {
+ case TypeCode.SByte:
+ return sbyte.Parse(text, _culture);
+ case TypeCode.Byte:
+ return byte.Parse(text, _culture);
+ case TypeCode.Int16:
+ return short.Parse(text, _culture);
+ case TypeCode.UInt16:
+ return ushort.Parse(text, _culture);
+ case TypeCode.Int32:
+ return int.Parse(text, _culture);
+ case TypeCode.UInt32:
+ return uint.Parse(text, _culture);
+ case TypeCode.Int64:
+ return long.Parse(text, _culture);
+ case TypeCode.UInt64:
+ return ulong.Parse(text, _culture);
+ case TypeCode.Single:
+ return float.Parse(text, _culture);
+ case TypeCode.Double:
+ return double.Parse(text, _culture);
+ case TypeCode.Decimal:
+ return decimal.Parse(text, _culture);
+ }
#else
var tp = TypeHelper.GetNonNullableType(type);
if (tp == typeof(sbyte))
@@ -289,28 +279,27 @@ public bool TryParseNumber(string text, Type type, out object? result)
return decimal.Parse(text, _culture);
}
#endif
- }
- catch
- {
- return null;
- }
-
+ }
+ catch
+ {
return null;
}
- private Expression ParseAsBinary(int tokenPosition, string text, bool isNegative)
- {
- if (RegexBinary32.IsMatch(text))
- {
- return _constantExpressionHelper.CreateLiteral((isNegative ? -1 : 1) * Convert.ToInt32(text, 2), text);
- }
+ return null;
+ }
- if (RegexBinary64.IsMatch(text))
- {
- return _constantExpressionHelper.CreateLiteral((isNegative ? -1 : 1) * Convert.ToInt64(text, 2), text);
- }
+ private Expression ParseAsBinary(int tokenPosition, string text, string textOriginal, bool isNegative)
+ {
+ if (RegexBinary32.IsMatch(text))
+ {
+ return _constantExpressionHelper.CreateLiteral((isNegative ? -1 : 1) * Convert.ToInt32(text, 2), textOriginal);
+ }
- throw new ParseException(string.Format(_culture, Res.InvalidBinaryIntegerLiteral, text), tokenPosition);
+ if (RegexBinary64.IsMatch(text))
+ {
+ return _constantExpressionHelper.CreateLiteral((isNegative ? -1 : 1) * Convert.ToInt64(text, 2), textOriginal);
}
+
+ throw new ParseException(string.Format(_culture, Res.InvalidBinaryIntegerLiteral, text), tokenPosition);
}
-}
+}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs b/src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs
index 19002c4f..f4401b63 100644
--- a/src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs
@@ -83,20 +83,20 @@ public static bool IsCompatibleWith(Type source, Type target)
return target.IsAssignableFrom(source);
}
- Type st = GetNonNullableType(source);
- Type tt = GetNonNullableType(target);
+ var sourceType = GetNonNullableType(source);
+ var targetType = GetNonNullableType(target);
- if (st != source && tt == target)
+ if (sourceType != source && targetType == target)
{
return false;
}
- TypeCode sc = st.GetTypeInfo().IsEnum ? TypeCode.Int64 : Type.GetTypeCode(st);
- TypeCode tc = tt.GetTypeInfo().IsEnum ? TypeCode.Int64 : Type.GetTypeCode(tt);
- switch (sc)
+ var sourceTypeCode = sourceType.GetTypeInfo().IsEnum ? TypeCode.Int32 : Type.GetTypeCode(sourceType);
+ var targetTypeCode = targetType.GetTypeInfo().IsEnum ? TypeCode.Int32 : Type.GetTypeCode(targetType);
+ switch (sourceTypeCode)
{
case TypeCode.SByte:
- switch (tc)
+ switch (targetTypeCode)
{
case TypeCode.SByte:
case TypeCode.Int16:
@@ -110,7 +110,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;
case TypeCode.Byte:
- switch (tc)
+ switch (targetTypeCode)
{
case TypeCode.Byte:
case TypeCode.Int16:
@@ -127,7 +127,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;
case TypeCode.Int16:
- switch (tc)
+ switch (targetTypeCode)
{
case TypeCode.Int16:
case TypeCode.Int32:
@@ -140,7 +140,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;
case TypeCode.UInt16:
- switch (tc)
+ switch (targetTypeCode)
{
case TypeCode.UInt16:
case TypeCode.Int32:
@@ -155,7 +155,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;
case TypeCode.Int32:
- switch (tc)
+ switch (targetTypeCode)
{
case TypeCode.Int32:
case TypeCode.Int64:
@@ -167,7 +167,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;
case TypeCode.UInt32:
- switch (tc)
+ switch (targetTypeCode)
{
case TypeCode.UInt32:
case TypeCode.Int64:
@@ -180,7 +180,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;
case TypeCode.Int64:
- switch (tc)
+ switch (targetTypeCode)
{
case TypeCode.Int64:
case TypeCode.Single:
@@ -191,7 +191,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;
case TypeCode.UInt64:
- switch (tc)
+ switch (targetTypeCode)
{
case TypeCode.UInt64:
case TypeCode.Single:
@@ -202,7 +202,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;
case TypeCode.Single:
- switch (tc)
+ switch (targetTypeCode)
{
case TypeCode.Single:
case TypeCode.Double:
@@ -211,7 +211,7 @@ public static bool IsCompatibleWith(Type source, Type target)
break;
default:
- if (st == tt)
+ if (sourceType == targetType)
{
return true;
}
@@ -471,6 +471,11 @@ public static Type GetUnderlyingType(Type type)
return type;
}
+ public static bool TypesAreEqual(Type type, Type typeToCheck)
+ {
+ return GetNullableType(type) == GetNullableType(typeToCheck);
+ }
+
public static IList GetSelfAndBaseTypes(Type type, bool excludeObject = false)
{
if (type.GetTypeInfo().IsInterface)
diff --git a/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj b/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj
index bcc32ad0..1627393c 100644
--- a/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj
+++ b/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj
@@ -9,8 +9,8 @@
This is a .NETStandard / .NET Core port of the the Microsoft assembly for the .Net 4.0 Dynamic language functionality.
system;linq;dynamic;core;dotnet;NETCoreApp;NETStandard
{D3804228-91F4-4502-9595-39584E510002}
- net35;net40;net45;net452;net46;netstandard1.3;netstandard2.0;netstandard2.1;uap10.0;netcoreapp2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0
- 1.6.$(PatchVersion)
+ net35;net40;net45;net452;net46;netstandard1.3;netstandard2.0;netstandard2.1;uap10.0;netcoreapp2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0
+ 1.7.$(PatchVersion)
@@ -34,7 +34,7 @@
$(DefineConstants);NETSTANDARD
-
+
$(DefineConstants);ASYNCENUMERABLE
diff --git a/src/Z.EntityFramework.Classic.DynamicLinq/Z.EntityFramework.Classic.DynamicLinq.csproj b/src/Z.EntityFramework.Classic.DynamicLinq/Z.EntityFramework.Classic.DynamicLinq.csproj
index 8cf07a98..4edc3a0e 100644
--- a/src/Z.EntityFramework.Classic.DynamicLinq/Z.EntityFramework.Classic.DynamicLinq.csproj
+++ b/src/Z.EntityFramework.Classic.DynamicLinq/Z.EntityFramework.Classic.DynamicLinq.csproj
@@ -10,7 +10,7 @@
system;linq;dynamic;Z.EntityFramework;core;async;classic
{D3804228-91F4-4502-9595-39584Ea20000}
net45;netstandard2.0
- 1.6.$(PatchVersion)
+ 1.7.$(PatchVersion)
diff --git a/test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj b/test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj
index bb2d9ae6..88c88664 100644
--- a/test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj
+++ b/test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj
@@ -2,7 +2,7 @@
Stef Heyenrath
- net461;net8.0;net9.0
+ net461;net8.0;net9.0;net10.0
full
EF;NET461
EntityFramework.DynamicLinq.Tests
@@ -40,7 +40,7 @@
-
+
@@ -58,20 +58,25 @@
-
+
-
+
-
+
-
+
+
+
+
+
+
$(DefineConstants);AspNetCoreIdentity
diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs
index 2e94a212..759f156f 100644
--- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs
+++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/NewtonsoftJsonTests.cs
@@ -1,4 +1,5 @@
-using FluentAssertions;
+using System.Linq.Dynamic.Core.NewtonsoftJson.Config;
+using FluentAssertions;
using Newtonsoft.Json.Linq;
using Xunit;
@@ -11,11 +12,13 @@ public class NewtonsoftJsonTests
[
{
"Name": "John",
- "Age": 30
+ "Age": 30,
+ "IsNull": null
},
{
"Name": "Doe",
- "Age": 40
+ "Age": 40,
+ "AlsoNull": null
}
]
""";
@@ -506,4 +509,67 @@ public void Where_With_Select()
var first = result.First();
first.Value().Should().Be("Doe");
}
+
+ [Theory]
+ [InlineData("notExisting == true")]
+ [InlineData("notExisting == \"true\"")]
+ [InlineData("notExisting == 1")]
+ [InlineData("notExisting == \"1\"")]
+ [InlineData("notExisting == \"something\"")]
+ [InlineData("notExisting > 1")]
+ [InlineData("notExisting < 1")]
+ [InlineData("true == notExisting")]
+ [InlineData("\"true\" == notExisting")]
+ [InlineData("1 == notExisting")]
+ [InlineData("\"1\" == notExisting")]
+ [InlineData("\"something\" == notExisting")]
+ [InlineData("1 < notExisting")]
+ [InlineData("1 > notExisting")]
+ public void Where_NonExistingMember_EmptyResult(string predicate)
+ {
+ // Arrange
+ var config = new NewtonsoftJsonParsingConfig
+ {
+ ConvertObjectToSupportComparison = true
+ };
+
+ // Act
+ var result = _source.Where(config, predicate);
+
+ // Assert
+ result.Should().BeEmpty();
+ }
+
+ [Theory]
+ [InlineData("""[ { "Name": "John", "Age": 30 }, { "Name": "Doe" }, { } ]""")]
+ [InlineData("""[ { "Name": "Doe" }, { "Name": "John", "Age": 30 }, { } ]""")]
+ public void NormalizeArray(string array)
+ {
+ // Act
+ var result = JArray.Parse(array)
+ .Where("Age >= 30")
+ .Select("Name");
+
+ // Assert
+ result.Should().HaveCount(1);
+ var first = result.First();
+ first.Value().Should().Be("John");
+ }
+
+ [Fact]
+ public void NormalizeArray_When_NormalizeIsFalse_ShouldThrow()
+ {
+ // Arrange
+ var config = new NewtonsoftJsonParsingConfig
+ {
+ Normalize = false
+ };
+ var array = """[ { "Name": "Doe" }, { "Name": "John", "Age": 30 }, { } ]""";
+
+ // Act
+ Action act = () => JArray.Parse(array).Where(config, "Age >= 30");
+
+ // Assert
+ act.Should().Throw().WithMessage("Unable to find property 'Age' on type '<>*");
+ }
}
\ No newline at end of file
diff --git a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj
index 4882ff18..57de2628 100644
--- a/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj
+++ b/test/System.Linq.Dynamic.Core.NewtonsoftJson.Tests/System.Linq.Dynamic.Core.NewtonsoftJson.Tests.csproj
@@ -1,7 +1,7 @@
Stef Heyenrath
- net452;netcoreapp3.1;net8.0
+ net452;netcoreapp3.1;net8.0;net10.0
full
True
latest
diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/DynamicExpressionParserTests.cs
index 6e174735..ed3d4f95 100644
--- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/DynamicExpressionParserTests.cs
+++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/DynamicExpressionParserTests.cs
@@ -2,7 +2,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Text.Json;
-using FluentAssertions;
+using AwesomeAssertions;
namespace System.Linq.Dynamic.Core.SystemTextJson.Tests;
diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/Extensions/JsonDocumentExtensionsTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/Extensions/JsonDocumentExtensionsTests.cs
index 048f7f3a..5c3698dc 100644
--- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/Extensions/JsonDocumentExtensionsTests.cs
+++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/Extensions/JsonDocumentExtensionsTests.cs
@@ -1,7 +1,7 @@
using System.Linq.Dynamic.Core.SystemTextJson.Extensions;
using System.Text;
using System.Text.Json;
-using FluentAssertions;
+using AwesomeAssertions;
using Xunit;
namespace System.Linq.Dynamic.Core.SystemTextJson.Tests.Extensions;
diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj
index dd619154..bfb5547e 100644
--- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj
+++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/System.Linq.Dynamic.Core.SystemTextJson.Tests.csproj
@@ -1,7 +1,7 @@
Stef Heyenrath
- net6.0;net8.0
+ net6.0;net8.0;net10.0
full
True
latest
@@ -11,7 +11,7 @@
-
+
all
diff --git a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs
index f5dee221..9a81f76a 100644
--- a/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs
+++ b/test/System.Linq.Dynamic.Core.SystemTextJson.Tests/SystemTextJsonTests.cs
@@ -1,5 +1,6 @@
-using System.Text.Json;
-using FluentAssertions;
+using System.Linq.Dynamic.Core.SystemTextJson.Config;
+using System.Text.Json;
+using AwesomeAssertions;
using Xunit;
namespace System.Linq.Dynamic.Core.SystemTextJson.Tests;
@@ -16,11 +17,13 @@ public class SystemTextJsonTests
[
{
"Name": "John",
- "Age": 30
+ "Age": 30,
+ "IsNull": null
},
{
"Name": "Doe",
- "Age": 40
+ "Age": 40,
+ "AlsoNull": null
}
]
""";
@@ -160,20 +163,20 @@ public void Distinct()
public void First()
{
// Act + Assert 1
- _source.First().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30}").RootElement.GetRawText());
+ _source.First().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
// Act + Assert 2
- _source.First("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
+ _source.First("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
}
[Fact]
public void FirstOrDefault()
{
// Act + Assert 1
- _source.FirstOrDefault()!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30}").RootElement.GetRawText());
+ _source.FirstOrDefault()!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
// Act + Assert 2
- _source.FirstOrDefault("Age > 30")!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
+ _source.FirstOrDefault("Age > 30")!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
// Act + Assert 3
_source.FirstOrDefault("Age > 999").Should().BeNull();
@@ -266,20 +269,20 @@ public void GroupBySimpleKeySelector()
public void Last()
{
// Act + Assert 1
- _source.Last().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
+ _source.Last().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
// Act + Assert 2
- _source.Last("Age > 0").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
+ _source.Last("Age > 0").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
}
[Fact]
public void LastOrDefault()
{
// Act + Assert 1
- _source.LastOrDefault()!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
+ _source.LastOrDefault()!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
// Act + Assert 2
- _source.LastOrDefault("Age > 0")!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
+ _source.LastOrDefault("Age > 0")!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
// Act + Assert 3
_source.LastOrDefault("Age > 999").Should().BeNull();
@@ -443,7 +446,7 @@ public void SelectMany()
public void Single()
{
// Act + Assert
- _source.Single("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
+ _source.Single("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
}
[Fact]
@@ -535,4 +538,65 @@ public void Where_With_Select()
array.Should().HaveCount(1);
array.First().GetString().Should().Be("Doe");
}
+
+ [Theory]
+ [InlineData("notExisting == true")]
+ [InlineData("notExisting == \"true\"")]
+ [InlineData("notExisting == 1")]
+ [InlineData("notExisting == \"1\"")]
+ [InlineData("notExisting == \"something\"")]
+ [InlineData("notExisting > 1")]
+ [InlineData("notExisting < 1")]
+ [InlineData("true == notExisting")]
+ [InlineData("\"true\" == notExisting")]
+ [InlineData("1 == notExisting")]
+ [InlineData("\"1\" == notExisting")]
+ [InlineData("\"something\" == notExisting")]
+ [InlineData("1 < notExisting")]
+ [InlineData("1 > notExisting")]
+ public void Where_NonExistingMember_EmptyResult(string predicate)
+ {
+ // Act
+ var result = _source.Where(predicate).RootElement.EnumerateArray();
+
+ // Assert
+ result.Should().BeEmpty();
+ }
+
+ [Theory]
+ [InlineData("""[ { "Name": "John", "Age": 30 }, { "Name": "Doe" }, { } ]""")]
+ [InlineData("""[ { "Name": "Doe" }, { "Name": "John", "Age": 30 }, { } ]""")]
+ public void NormalizeArray(string data)
+ {
+ // Arrange
+ var source = JsonDocument.Parse(data);
+
+ // Act
+ var result = source
+ .Where("Age >= 30")
+ .Select("Name");
+
+ // Assert
+ var array = result.RootElement.EnumerateArray();
+ array.Should().HaveCount(1);
+ var first = result.First();
+ array.First().GetString().Should().Be("John");
+ }
+
+ [Fact]
+ public void NormalizeArray_When_NormalizeIsFalse_ShouldThrow()
+ {
+ // Arrange
+ var config = new SystemTextJsonParsingConfig
+ {
+ Normalize = false
+ };
+ var data = """[ { "Name": "Doe" }, { "Name": "John", "Age": 30 }, { } ]""";
+
+ // Act
+ Action act = () => JsonDocument.Parse(data).Where(config, "Age >= 30");
+
+ // Assert
+ act.Should().Throw().WithMessage("Unable to find property 'Age' on type '<>f__AnonymousType*");
+ }
}
\ No newline at end of file
diff --git a/test/System.Linq.Dynamic.Core.Tests.Net5/System.Linq.Dynamic.Core.Tests.Net5.csproj b/test/System.Linq.Dynamic.Core.Tests.Net5/System.Linq.Dynamic.Core.Tests.Net5.csproj
index 7e8ccc72..47b4bb3d 100644
--- a/test/System.Linq.Dynamic.Core.Tests.Net5/System.Linq.Dynamic.Core.Tests.Net5.csproj
+++ b/test/System.Linq.Dynamic.Core.Tests.Net5/System.Linq.Dynamic.Core.Tests.Net5.csproj
@@ -17,7 +17,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
diff --git a/test/System.Linq.Dynamic.Core.Tests.Net6/System.Linq.Dynamic.Core.Tests.Net6.csproj b/test/System.Linq.Dynamic.Core.Tests.Net6/System.Linq.Dynamic.Core.Tests.Net6.csproj
index c20f741c..0550f818 100644
--- a/test/System.Linq.Dynamic.Core.Tests.Net6/System.Linq.Dynamic.Core.Tests.Net6.csproj
+++ b/test/System.Linq.Dynamic.Core.Tests.Net6/System.Linq.Dynamic.Core.Tests.Net6.csproj
@@ -16,7 +16,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
diff --git a/test/System.Linq.Dynamic.Core.Tests.Net7/System.Linq.Dynamic.Core.Tests.Net7.csproj b/test/System.Linq.Dynamic.Core.Tests.Net7/System.Linq.Dynamic.Core.Tests.Net7.csproj
index 6d415bb0..90fd9b01 100644
--- a/test/System.Linq.Dynamic.Core.Tests.Net7/System.Linq.Dynamic.Core.Tests.Net7.csproj
+++ b/test/System.Linq.Dynamic.Core.Tests.Net7/System.Linq.Dynamic.Core.Tests.Net7.csproj
@@ -16,7 +16,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
diff --git a/test/System.Linq.Dynamic.Core.Tests.Net8/System.Linq.Dynamic.Core.Tests.Net8.csproj b/test/System.Linq.Dynamic.Core.Tests.Net8/System.Linq.Dynamic.Core.Tests.Net8.csproj
index 978abb1f..a6dda0af 100644
--- a/test/System.Linq.Dynamic.Core.Tests.Net8/System.Linq.Dynamic.Core.Tests.Net8.csproj
+++ b/test/System.Linq.Dynamic.Core.Tests.Net8/System.Linq.Dynamic.Core.Tests.Net8.csproj
@@ -16,7 +16,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
@@ -31,7 +31,7 @@
-
+
diff --git a/test/System.Linq.Dynamic.Core.Tests.Net9/System.Linq.Dynamic.Core.Tests.Net9.csproj b/test/System.Linq.Dynamic.Core.Tests.Net9/System.Linq.Dynamic.Core.Tests.Net9.csproj
new file mode 100644
index 00000000..f7cb2fa7
--- /dev/null
+++ b/test/System.Linq.Dynamic.Core.Tests.Net9/System.Linq.Dynamic.Core.Tests.Net9.csproj
@@ -0,0 +1,48 @@
+
+
+
+ net9.0
+ System.Linq.Dynamic.Core.Tests
+ full
+ True
+ ../../src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.snk
+ false
+ $(DefineConstants);NETCOREAPP;EFCORE;EFCORE_3X;NETCOREAPP3_1;AspNetCoreIdentity
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/System.Linq.Dynamic.Core.Tests.NetCoreApp31/System.Linq.Dynamic.Core.Tests.NetCoreApp31.csproj b/test/System.Linq.Dynamic.Core.Tests.NetCoreApp31/System.Linq.Dynamic.Core.Tests.NetCoreApp31.csproj
index 5b908293..76de3f36 100644
--- a/test/System.Linq.Dynamic.Core.Tests.NetCoreApp31/System.Linq.Dynamic.Core.Tests.NetCoreApp31.csproj
+++ b/test/System.Linq.Dynamic.Core.Tests.NetCoreApp31/System.Linq.Dynamic.Core.Tests.NetCoreApp31.csproj
@@ -22,7 +22,7 @@
all
runtime; build; native; contentfiles; analyzers
-
+
diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs
index 32dd3002..066d5ce4 100644
--- a/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/DynamicClassTest.cs
@@ -109,7 +109,11 @@ public void DynamicClass_GetPropertyValue_Should_Work()
value.Should().Be(test);
}
+#if NET461
+ [Fact(Skip = "Does not work for .NET 4.6.1")]
+#else
[Fact]
+#endif
public void DynamicClass_GettingValue_ByIndex_Should_Work()
{
// Arrange
@@ -127,27 +131,39 @@ public void DynamicClass_GettingValue_ByIndex_Should_Work()
value.Should().Be(test);
}
+#if NET461
+ [Fact(Skip = "Does not work for .NET 4.6.1")]
+#else
[Fact]
+#endif
public void DynamicClass_SettingExistingPropertyValue_ByIndex_Should_Work()
{
// Arrange
- var test = "Test";
- var newTest = "abc";
- var range = new List