From b08d8f6d4eb332ea47d3cbca34ef652a87cb52a9 Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Wed, 12 Nov 2025 21:12:27 +1000 Subject: [PATCH 01/13] Add tests for .NET 10 (#258) +semver:patch --- .github/copilot-instructions.md | 1 + .github/workflows/dotnet-linux.yml | 2 + .github/workflows/dotnet-windows.yml | 1 + DelegateDecompiler.sln | 6 + global.json | 2 +- ...ompiler.EntityFrameworkCore10.Tests.csproj | 45 + .../DetailedListOfSupportedCommands.md | 154 ++++ .../DetailedListOfSupportedCommandsWithSQL.md | 777 ++++++++++++++++++ .../DocumentationHeaderText.md | 20 + .../SummaryOfSupportedCommands.md | 62 ++ .../Test file - please ignore.txt | 1 + test.cmd | 4 +- 12 files changed, 1073 insertions(+), 2 deletions(-) create mode 100644 src/DelegateDecompiler.EntityFrameworkCore10.Tests/DelegateDecompiler.EntityFrameworkCore10.Tests.csproj create mode 100644 src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/DetailedListOfSupportedCommands.md create mode 100644 src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/DetailedListOfSupportedCommandsWithSQL.md create mode 100644 src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/DocumentationHeaderText.md create mode 100644 src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/SummaryOfSupportedCommands.md create mode 100644 src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/Test file - please ignore.txt diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 3b4cb053..28379093 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -27,6 +27,7 @@ dotnet test -c Debug -f net8.0 src/DelegateDecompiler.EntityFramework.Tests dotnet test -c Debug -f net8.0 src/DelegateDecompiler.EntityFrameworkCore6.Tests dotnet test -c Debug -f net8.0 src/DelegateDecompiler.EntityFrameworkCore8.Tests dotnet test -c Debug -f net9.0 src/DelegateDecompiler.EntityFrameworkCore9.Tests +dotnet test -c Debug -f net10.0 src\DelegateDecompiler.EntityFrameworkCore10.Tests ``` Use `-p:DisableGitVersionTask=true` flag to avoid build issues if GitVersion is not set up. diff --git a/.github/workflows/dotnet-linux.yml b/.github/workflows/dotnet-linux.yml index a98f031a..cfa433d3 100644 --- a/.github/workflows/dotnet-linux.yml +++ b/.github/workflows/dotnet-linux.yml @@ -41,6 +41,7 @@ jobs: - name: Restore run: dotnet restore + - name: Patch App.config run: | $configPath = 'src/DelegateDecompiler.EntityFramework.Tests/App.config' @@ -60,3 +61,4 @@ jobs: dotnet test --no-build -c Release -f net8.0 src/DelegateDecompiler.EntityFrameworkCore6.Tests dotnet test --no-build -c Release -f net8.0 src/DelegateDecompiler.EntityFrameworkCore8.Tests dotnet test --no-build -c Release -f net9.0 src/DelegateDecompiler.EntityFrameworkCore9.Tests + dotnet test --no-build -c Release -f net10.0 src\DelegateDecompiler.EntityFrameworkCore10.Tests diff --git a/.github/workflows/dotnet-windows.yml b/.github/workflows/dotnet-windows.yml index b57da003..f9988079 100644 --- a/.github/workflows/dotnet-windows.yml +++ b/.github/workflows/dotnet-windows.yml @@ -55,3 +55,4 @@ jobs: dotnet test --no-build -c Release -f net8.0 src/DelegateDecompiler.EntityFrameworkCore6.Tests dotnet test --no-build -c Release -f net8.0 src/DelegateDecompiler.EntityFrameworkCore8.Tests dotnet test --no-build -c Release -f net9.0 src/DelegateDecompiler.EntityFrameworkCore9.Tests + dotnet test --no-build -c Release -f net10.0 src\DelegateDecompiler.EntityFrameworkCore10.Tests diff --git a/DelegateDecompiler.sln b/DelegateDecompiler.sln index 0756e7e4..285f7be7 100644 --- a/DelegateDecompiler.sln +++ b/DelegateDecompiler.sln @@ -35,6 +35,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DelegateDecompiler.EntityFr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DelegateDecompiler.EntityFrameworkCore9.Tests", "src\DelegateDecompiler.EntityFrameworkCore9.Tests\DelegateDecompiler.EntityFrameworkCore9.Tests.csproj", "{1E051041-B9CB-44AC-B2A2-5D4A4B91B2DB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DelegateDecompiler.EntityFrameworkCore10.Tests", "src\DelegateDecompiler.EntityFrameworkCore10.Tests\DelegateDecompiler.EntityFrameworkCore10.Tests.csproj", "{5C3905CC-6B38-4C95-80FC-0DB5B72C5ABD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -77,6 +79,10 @@ Global {1E051041-B9CB-44AC-B2A2-5D4A4B91B2DB}.Debug|Any CPU.Build.0 = Debug|Any CPU {1E051041-B9CB-44AC-B2A2-5D4A4B91B2DB}.Release|Any CPU.ActiveCfg = Release|Any CPU {1E051041-B9CB-44AC-B2A2-5D4A4B91B2DB}.Release|Any CPU.Build.0 = Release|Any CPU + {5C3905CC-6B38-4C95-80FC-0DB5B72C5ABD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C3905CC-6B38-4C95-80FC-0DB5B72C5ABD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C3905CC-6B38-4C95-80FC-0DB5B72C5ABD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C3905CC-6B38-4C95-80FC-0DB5B72C5ABD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/global.json b/global.json index cdbb589e..512142d2 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100", + "version": "10.0.100", "rollForward": "latestFeature" } } diff --git a/src/DelegateDecompiler.EntityFrameworkCore10.Tests/DelegateDecompiler.EntityFrameworkCore10.Tests.csproj b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/DelegateDecompiler.EntityFrameworkCore10.Tests.csproj new file mode 100644 index 00000000..7ee69f0b --- /dev/null +++ b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/DelegateDecompiler.EntityFrameworkCore10.Tests.csproj @@ -0,0 +1,45 @@ + + + net10.0 + Copyright © Dave Glick 2014, Jon Smith 2014, Alexander Zaytsev 2014 - 2024 + $(DefineConstants);EF_CORE;EF_CORE3;EF_CORE5 + DelegateDecompiler.EntityFramework.Tests + + + + + + %(Name) + + + + + + %(Name) + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/DetailedListOfSupportedCommands.md b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/DetailedListOfSupportedCommands.md new file mode 100644 index 00000000..be4f2dba --- /dev/null +++ b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/DetailedListOfSupportedCommands.md @@ -0,0 +1,154 @@ +Detail of supported commands +============ +## Documentation produced for DelegateDecompiler, version 0.34.3.0 on Wednesday, 05 March 2025 18:24 + +This file documents what linq commands **DelegateDecompiler** supports when +working with [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/) (EF). +EF has one of the best implementations for converting Linq `IQueryable<>` commands into database +access commands, in EF's case T-SQL. Therefore it is a good candidate for using in our tests. + +This documentation was produced by compaired direct EF Linq queries against the same query implemented +as a DelegateDecompiler's `Computed` properties. This produces a Supported/Not Supported flag +on each command type tested. Tests are groups and ordered to try and make finding things +easier. + +So, if you want to use DelegateDecompiler and are not sure whether the linq command +you want to use will work then clone this project and write your own tests. +(See [How to add a test](HowToAddMoreTests.md) documentation on how to do this). +If there is a problem then please fork the repository and add your own tests. +That will make it much easier to diagnose your issue. + +*Note: The test suite has only recently been set up and has only a handful of tests at the moment. +More will appear as we move forward.* + + +### Group: Basic Features +#### [Select](../TestGroup05BasicFeatures/Test01Select.cs): +- Supported + * Bool Equals Constant (line 38) + * Bool Equals Static Variable (line 61) + * Int Equals Constant (line 82) + * Select Property Without Computed Attribute (line 103) + * Select Method Without Computed Attribute (line 124) + * Select Abstract Member Over Tph Hierarchy (line 145) + * Select Abstract Member Over Tph Hierarchy After Restricting To Subtype (line 166) + * Select Abstract Member Over Tph Hierarchy With Generic Classes After Restricting To Subtype (line 184) + * Select Abstract Member With Condition On It Over Tph Hierarchy With Generic Classes After Restricting To Subtype (line 212) + * Select Multiple Levels Of Abstract Members Over Tph Hierarchy (line 234) + * Select Select Many (line 256) + +#### [Select Async](../TestGroup05BasicFeatures/Test02SelectAsync.cs): +- Supported + * Async (line 43) + * Bool Equals Constant Async (line 83) + * Decompile Upfront Bool Equals Constant Async (line 104) + * Bool Equals Static Variable To Array Async (line 127) + * Int Equals Constant (line 148) + +#### [Equals And Not Equals](../TestGroup05BasicFeatures/Test03EqualsAndNotEquals.cs): +- Supported + * Int Equals Constant (line 36) + * Int Equals Static Variable (line 58) + * Int Equals String Length (line 79) + * Int Not Equals String Length (line 100) + +#### [Nullable](../TestGroup05BasicFeatures/Test04Nullable.cs): +- Supported + * Property Is Null (line 39) + * Bool Equals Static Variable (line 62) + * Int Equals Constant (line 83) + * Nullable Init (line 104) + * Nullable Add (line 125) + +#### [Where](../TestGroup05BasicFeatures/Test05Where.cs): +- Supported + * Where Bool Equals Constant (line 37) + * Where Bool Equals Static Variable (line 60) + * Where Int Equals Constant (line 81) + * Where Filters On Abstract Members Over Tph Hierarchy (line 102) + +#### [Single](../TestGroup05BasicFeatures/Test10Single.cs): +- Supported + * Single Int Equals Unique Value (line 42) + +#### [Single Async](../TestGroup05BasicFeatures/Test11SingleAsync.cs): +- Supported + * Single Int Equals Unique Value Async (line 48) + + +### Group: Order Take +#### [Order By](../TestGroup10OrderTake/Test01OrderBy.cs): +- Supported + * Order By Children Count (line 37) + * Order By Children Count Then By String Length (line 59) + * Where Any Children Then Order By Children Count (line 81) + +#### [Skip Take](../TestGroup10OrderTake/Test02SkipTake.cs): +- Supported + * Order By Children Count Then Take (line 37) + * Order By Children Count Then Skip And Take (line 59) + * Where Any Children Then Order By Then Skip Take (line 81) + + +### Group: Quantifier Operators +#### [Any](../TestGroup12QuantifierOperators/Test01Any.cs): +- Supported + * Any Children (line 36) + * Any Children With Filter (line 57) + +#### [All](../TestGroup12QuantifierOperators/Test02All.cs): +- Supported + * Singleton All Filter (line 36) + * All Filter On Children Int (line 57) + +#### [Contains](../TestGroup12QuantifierOperators/Test03Contains.cs): +- Supported + * String Contains Constant String With Filter (line 37) + + +### Group: Aggregation +#### [Count](../TestGroup15Aggregation/Test01Count.cs): +- Supported + * Count Children (line 37) + * Count Children With Filter (line 59) + * Count Children With Filter By Closure (line 81) + * Count Children With Filter By External Closure (line 104) + * Count Children With Filter By External Closure2 (line 128) + * Singleton Count Children With Filter (line 150) + +#### [Sum](../TestGroup15Aggregation/Test02Sum.cs): +- Supported + * Sum Count In Children Where Children Can Be None (line 61) + +#### [Count Async](../TestGroup15Aggregation/Test03CountAsync.cs): +- Supported + * Count Children Async (line 44) + * Count Children With Filter Async (line 66) + * Count Children With Filter By Closure Async (line 88) + * Count Children With Filter By External Closure Async (line 111) + * Count Children With Filter By External Closure2 Async (line 135) + * Singleton Count Children With Filter Async (line 157) + + +### Group: Types +#### [Strings](../TestGroup50Types/Test01Strings.cs): +- Supported + * Concatenate Person Not Handle Null (line 36) + * Concatenate Person Handle Null (line 57) + * Concatenate Person Handle Name Order (line 80) + * Select Generic Method Person Handle (line 101) + * Filter Generic Method Person Handle (line 119) + +#### [DateTime](../TestGroup50Types/Test05DateTime.cs): +- Supported + * DateTime Where Compare With Static Variable (line 39) + + +### Group: Additional Features +#### [Nested Expressions](../TestGroup90AdditionalFeatures/Test01NestedExpressions.cs): +- Supported + * Subquery As Context Extension Method (line 72) + + + +The End diff --git a/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/DetailedListOfSupportedCommandsWithSQL.md b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/DetailedListOfSupportedCommandsWithSQL.md new file mode 100644 index 00000000..b87dd96f --- /dev/null +++ b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/DetailedListOfSupportedCommandsWithSQL.md @@ -0,0 +1,777 @@ +Detail With Sql of supported commands +============ +## Documentation produced for DelegateDecompiler, version 0.34.3.0 on Wednesday, 05 March 2025 18:24 + +This file documents what linq commands **DelegateDecompiler** supports when +working with [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/) (EF). +EF has one of the best implementations for converting Linq `IQueryable<>` commands into database +access commands, in EF's case T-SQL. Therefore it is a good candidate for using in our tests. + +This documentation was produced by compaired direct EF Linq queries against the same query implemented +as a DelegateDecompiler's `Computed` properties. This produces a Supported/Not Supported flag +on each command type tested. Tests are groups and ordered to try and make finding things +easier. + +So, if you want to use DelegateDecompiler and are not sure whether the linq command +you want to use will work then clone this project and write your own tests. +(See [How to add a test](HowToAddMoreTests.md) documentation on how to do this). +If there is a problem then please fork the repository and add your own tests. +That will make it much easier to diagnose your issue. + +*Note: The test suite has only recently been set up and has only a handful of tests at the moment. +More will appear as we move forward.* + + +### Group: Basic Features +#### [Select](../TestGroup05BasicFeatures/Test01Select.cs): +- Supported + * Bool Equals Constant (line 38) + * T-Sql executed is + +```SQL +SELECT [e].[ParentBool] +FROM [EfParents] AS [e] +``` + + * Bool Equals Static Variable (line 61) + * T-Sql executed is + +```SQL +SELECT ~([e].[ParentBool] ^ @staticBool) +FROM [EfParents] AS [e] +``` + + * Int Equals Constant (line 82) + * T-Sql executed is + +```SQL +SELECT ~CAST([e].[ParentInt] ^ 123 AS bit) +FROM [EfParents] AS [e] +``` + + * Select Property Without Computed Attribute (line 103) + * T-Sql executed is + +```SQL +SELECT [e].[FirstName] + N' ' + COALESCE([e].[MiddleName], N'') + N' ' + [e].[LastName] +FROM [EfPersons] AS [e] +``` + + * Select Method Without Computed Attribute (line 124) + * T-Sql executed is + +```SQL +SELECT [e].[FirstName] + N' ' + COALESCE([e].[MiddleName], N'') + N' ' + [e].[LastName] +FROM [EfPersons] AS [e] +``` + + * Select Abstract Member Over Tph Hierarchy (line 145) + * T-Sql executed is + +```SQL +SELECT CASE + WHEN [l].[Discriminator] = N'Person' THEN N'Human' + WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Carcharodon carcharias' + WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Gadus morhua' + WHEN [l].[Discriminator] = N'HoneyBee' THEN N'Apis mellifera' + WHEN [l].[Discriminator] = N'Dog' THEN N'Canis lupus' +END +FROM [LivingBeeing] AS [l] +``` + + * Select Abstract Member Over Tph Hierarchy After Restricting To Subtype (line 166) + * T-Sql executed is + +```SQL +SELECT CASE + WHEN [l].[Discriminator] = N'HoneyBee' THEN N'Apis mellifera' + WHEN [l].[Discriminator] = N'Dog' THEN N'Canis lupus' +END +FROM [LivingBeeing] AS [l] +WHERE [l].[Discriminator] IN (N'Dog', N'HoneyBee') +``` + + * Select Abstract Member Over Tph Hierarchy With Generic Classes After Restricting To Subtype (line 184) + * T-Sql executed is + +```SQL +SELECT CASE + WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Carcharodon carcharias' + WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Gadus morhua' +END AS [Species], CASE + WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Fish' + WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Fish' +END AS [Group] +FROM [LivingBeeing] AS [l] +WHERE [l].[Discriminator] IN (N'AtlanticCod', N'WhiteShark') +``` + + * Select Abstract Member With Condition On It Over Tph Hierarchy With Generic Classes After Restricting To Subtype (line 212) + * T-Sql executed is + +```SQL +SELECT CASE + WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Carcharodon carcharias' + WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Gadus morhua' +END AS [Species], CASE + WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Fish' + WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Fish' +END AS [Group] +FROM [LivingBeeing] AS [l] +WHERE [l].[Discriminator] IN (N'AtlanticCod', N'WhiteShark') AND CASE + WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Carcharodon carcharias' + WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Gadus morhua' +END IS NOT NULL AND CASE + WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Fish' + WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Fish' +END IS NOT NULL +``` + + * Select Multiple Levels Of Abstract Members Over Tph Hierarchy (line 234) + * T-Sql executed is + +```SQL +SELECT CASE + WHEN [l].[Discriminator] = N'HoneyBee' THEN N'Apis mellifera' + WHEN [l].[Discriminator] = N'Dog' THEN N'Canis lupus' +END, CASE + WHEN [l].[Discriminator] = N'HoneyBee' THEN CAST(0 AS bit) + WHEN [l].[Discriminator] = N'Dog' THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [LivingBeeing] AS [l] +WHERE [l].[Discriminator] IN (N'Dog', N'HoneyBee') +``` + + * Select Select Many (line 256) + * T-Sql executed is + +```SQL +SELECT [s0].[EfGrandChildId], [s0].[EfChildId], [s0].[GrandChildBool], [s0].[GrandChildDouble], [s0].[GrandChildInt], [s0].[GrandChildString] +FROM [EfParents] AS [e] +LEFT JOIN ( + SELECT [s].[EfGrandChildId], [s].[EfChildId], [s].[GrandChildBool], [s].[GrandChildDouble], [s].[GrandChildInt], [s].[GrandChildString], [s].[EfParentId] + FROM ( + SELECT [e1].[EfGrandChildId], [e1].[EfChildId], [e1].[GrandChildBool], [e1].[GrandChildDouble], [e1].[GrandChildInt], [e1].[GrandChildString], [e0].[EfParentId], ROW_NUMBER() OVER(PARTITION BY [e0].[EfParentId] ORDER BY [e1].[EfGrandChildId]) AS [row] + FROM [EfChildren] AS [e0] + INNER JOIN [EfGrandChildren] AS [e1] ON [e0].[EfChildId] = [e1].[EfChildId] + ) AS [s] + WHERE [s].[row] <= 1 +) AS [s0] ON [e].[EfParentId] = [s0].[EfParentId] +``` + + +#### [Select Async](../TestGroup05BasicFeatures/Test02SelectAsync.cs): +- Supported + * Async (line 43) + * T-Sql executed is + +```SQL +SELECT [e].[EfParentId], [e].[EndDate], [e].[ParentBool], [e].[ParentDouble], [e].[ParentInt], [e].[ParentNullableDecimal1], [e].[ParentNullableDecimal2], [e].[ParentNullableInt], [e].[ParentString], [e].[ParentTimeSpan], [e].[StartDate] +FROM [EfParents] AS [e] +``` + + * Bool Equals Constant Async (line 83) + * T-Sql executed is + +```SQL +SELECT [e].[ParentBool] +FROM [EfParents] AS [e] +``` + + * Decompile Upfront Bool Equals Constant Async (line 104) + * T-Sql executed is + +```SQL +SELECT [e].[ParentBool] +FROM [EfParents] AS [e] +``` + + * Bool Equals Static Variable To Array Async (line 127) + * T-Sql executed is + +```SQL +SELECT ~([e].[ParentBool] ^ @staticBool) +FROM [EfParents] AS [e] +``` + + * Int Equals Constant (line 148) + * T-Sql executed is + +```SQL +SELECT ~CAST([e].[ParentInt] ^ 123 AS bit) +FROM [EfParents] AS [e] +``` + + +#### [Equals And Not Equals](../TestGroup05BasicFeatures/Test03EqualsAndNotEquals.cs): +- Supported + * Int Equals Constant (line 36) + * T-Sql executed is + +```SQL +SELECT ~CAST([e].[ParentInt] ^ 123 AS bit) +FROM [EfParents] AS [e] +``` + + * Int Equals Static Variable (line 58) + * T-Sql executed is + +```SQL +SELECT ~CAST([e].[ParentInt] ^ @staticInt AS bit) +FROM [EfParents] AS [e] +``` + + * Int Equals String Length (line 79) + * T-Sql executed is + +```SQL +SELECT CASE + WHEN [e].[ParentInt] = CAST(LEN([e].[ParentString]) AS int) AND [e].[ParentString] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [EfParents] AS [e] +``` + + * Int Not Equals String Length (line 100) + * T-Sql executed is + +```SQL +SELECT CASE + WHEN [e].[ParentInt] <> CAST(LEN([e].[ParentString]) AS int) OR [e].[ParentString] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [EfParents] AS [e] +``` + + +#### [Nullable](../TestGroup05BasicFeatures/Test04Nullable.cs): +- Supported + * Property Is Null (line 39) + * T-Sql executed is + +```SQL +SELECT CASE + WHEN [e].[ParentNullableInt] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [EfParents] AS [e] +``` + + * Bool Equals Static Variable (line 62) + * T-Sql executed is + +```SQL +SELECT CASE + WHEN [e].[ParentNullableInt] IS NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [EfParents] AS [e] +``` + + * Int Equals Constant (line 83) + * T-Sql executed is + +```SQL +SELECT ~CAST(COALESCE([e].[ParentNullableInt], 0) ^ 123 AS bit) +FROM [EfParents] AS [e] +``` + + * Nullable Init (line 104) + * T-Sql executed is + +```SQL +SELECT NULL +FROM [EfParents] AS [e] +``` + + * Nullable Add (line 125) + * T-Sql executed is + +```SQL +SELECT [e].[ParentNullableDecimal1] + [e].[ParentNullableDecimal2] +FROM [EfParents] AS [e] +``` + + +#### [Where](../TestGroup05BasicFeatures/Test05Where.cs): +- Supported + * Where Bool Equals Constant (line 37) + * T-Sql executed is + +```SQL +SELECT [e].[EfParentId] +FROM [EfParents] AS [e] +WHERE [e].[ParentBool] = CAST(1 AS bit) +``` + + * Where Bool Equals Static Variable (line 60) + * T-Sql executed is + +```SQL +SELECT [e].[EfParentId] +FROM [EfParents] AS [e] +WHERE [e].[ParentBool] = @staticBool +``` + + * Where Int Equals Constant (line 81) + * T-Sql executed is + +```SQL +SELECT [e].[EfParentId] +FROM [EfParents] AS [e] +WHERE [e].[ParentInt] = 123 +``` + + * Where Filters On Abstract Members Over Tph Hierarchy (line 102) + * T-Sql executed is + +```SQL +SELECT [l].[Id] +FROM [LivingBeeing] AS [l] +WHERE CASE + WHEN [l].[Discriminator] = N'Person' THEN N'Human' + WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Carcharodon carcharias' + WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Gadus morhua' + WHEN [l].[Discriminator] = N'HoneyBee' THEN N'Apis mellifera' + WHEN [l].[Discriminator] = N'Dog' THEN N'Canis lupus' +END = N'Human' +``` + + +#### [Single](../TestGroup05BasicFeatures/Test10Single.cs): +- Supported + * Single Int Equals Unique Value (line 42) + * T-Sql executed is + +```SQL +SELECT TOP(2) [e].[EfParentId], ~CAST([e].[ParentInt] ^ 987 AS bit) AS [IntEqualsUniqueValue] +FROM [EfParents] AS [e] +WHERE [e].[ParentInt] = 987 +``` + + +#### [Single Async](../TestGroup05BasicFeatures/Test11SingleAsync.cs): +- Supported + * Single Int Equals Unique Value Async (line 48) + * T-Sql executed is + +```SQL +SELECT TOP(2) [e].[EfParentId], ~CAST([e].[ParentInt] ^ 987 AS bit) AS [IntEqualsUniqueValue] +FROM [EfParents] AS [e] +WHERE [e].[ParentInt] = 987 +``` + + + +### Group: Order Take +#### [Order By](../TestGroup10OrderTake/Test01OrderBy.cs): +- Supported + * Order By Children Count (line 37) + * T-Sql executed is + +```SQL +SELECT [e].[EfParentId] +FROM [EfParents] AS [e] +ORDER BY ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId]) +``` + + * Order By Children Count Then By String Length (line 59) + * T-Sql executed is + +```SQL +SELECT [e].[EfParentId] +FROM [EfParents] AS [e] +ORDER BY ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId]), CAST(LEN([e].[ParentString]) AS int) +``` + + * Where Any Children Then Order By Children Count (line 81) + * T-Sql executed is + +```SQL +SELECT [e].[EfParentId] +FROM [EfParents] AS [e] +WHERE EXISTS ( + SELECT 1 + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId]) +ORDER BY ( + SELECT COUNT(*) + FROM [EfChildren] AS [e1] + WHERE [e].[EfParentId] = [e1].[EfParentId]) +``` + + +#### [Skip Take](../TestGroup10OrderTake/Test02SkipTake.cs): +- Supported + * Order By Children Count Then Take (line 37) + * T-Sql executed is + +```SQL +SELECT TOP(@p) [e].[EfParentId] +FROM [EfParents] AS [e] +ORDER BY ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId]) +``` + + * Order By Children Count Then Skip And Take (line 59) + * T-Sql executed is + +```SQL +SELECT [e].[EfParentId] +FROM [EfParents] AS [e] +ORDER BY ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId]) +OFFSET @p ROWS FETCH NEXT @p0 ROWS ONLY +``` + + * Where Any Children Then Order By Then Skip Take (line 81) + * T-Sql executed is + +```SQL +SELECT [e].[EfParentId] +FROM [EfParents] AS [e] +WHERE EXISTS ( + SELECT 1 + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId]) +ORDER BY ( + SELECT COUNT(*) + FROM [EfChildren] AS [e1] + WHERE [e].[EfParentId] = [e1].[EfParentId]) +OFFSET @p ROWS FETCH NEXT @p ROWS ONLY +``` + + + +### Group: Quantifier Operators +#### [Any](../TestGroup12QuantifierOperators/Test01Any.cs): +- Supported + * Any Children (line 36) + * T-Sql executed is + +```SQL +SELECT CASE + WHEN EXISTS ( + SELECT 1 + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId]) THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [EfParents] AS [e] +``` + + * Any Children With Filter (line 57) + * T-Sql executed is + +```SQL +SELECT CASE + WHEN EXISTS ( + SELECT 1 + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId] AND [e0].[ChildInt] = 123) THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [EfParents] AS [e] +``` + + +#### [All](../TestGroup12QuantifierOperators/Test02All.cs): +- Supported + * Singleton All Filter (line 36) + * T-Sql executed is + +```SQL +SELECT CASE + WHEN NOT EXISTS ( + SELECT 1 + FROM [EfParents] AS [e] + WHERE [e].[ParentInt] <> 123) THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +``` + + * All Filter On Children Int (line 57) + * T-Sql executed is + +```SQL +SELECT CASE + WHEN NOT EXISTS ( + SELECT 1 + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId] AND [e0].[ChildInt] <> 123) THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [EfParents] AS [e] +``` + + +#### [Contains](../TestGroup12QuantifierOperators/Test03Contains.cs): +- Supported + * String Contains Constant String With Filter (line 37) + * T-Sql executed is + +```SQL +SELECT [e].[ParentString] +FROM [EfParents] AS [e] +WHERE [e].[ParentString] LIKE N'%2%' +``` + + + +### Group: Aggregation +#### [Count](../TestGroup15Aggregation/Test01Count.cs): +- Supported + * Count Children (line 37) + * T-Sql executed is + +```SQL +SELECT ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId]) +FROM [EfParents] AS [e] +``` + + * Count Children With Filter (line 59) + * T-Sql executed is + +```SQL +SELECT ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId] AND [e0].[ChildInt] = 123) +FROM [EfParents] AS [e] +``` + + * Count Children With Filter By Closure (line 81) + * T-Sql executed is + +```SQL +SELECT ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId] AND [e0].[ChildInt] = [e].[ParentInt]) +FROM [EfParents] AS [e] +``` + + * Count Children With Filter By External Closure (line 104) + * T-Sql executed is + +```SQL +SELECT ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId] AND [e0].[ChildInt] = @i) +FROM [EfParents] AS [e] +``` + + * Count Children With Filter By External Closure2 (line 128) + * T-Sql executed is + +```SQL +SELECT ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId] AND [e0].[ChildInt] = @i AND [e0].[EfParentId] = @j) +FROM [EfParents] AS [e] +``` + + * Singleton Count Children With Filter (line 150) + * T-Sql executed is + +```SQL +SELECT COUNT(*) +FROM [EfParents] AS [e] +WHERE ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId]) = 2 +``` + + +#### [Sum](../TestGroup15Aggregation/Test02Sum.cs): +- Supported + * Sum Count In Children Where Children Can Be None (line 61) + * T-Sql executed is + +```SQL +SELECT COALESCE(( + SELECT COALESCE(SUM([e0].[ChildInt]), 0) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId]), 0) +FROM [EfParents] AS [e] +``` + + +#### [Count Async](../TestGroup15Aggregation/Test03CountAsync.cs): +- Supported + * Count Children Async (line 44) + * T-Sql executed is + +```SQL +SELECT ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId]) +FROM [EfParents] AS [e] +``` + + * Count Children With Filter Async (line 66) + * T-Sql executed is + +```SQL +SELECT ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId] AND [e0].[ChildInt] = 123) +FROM [EfParents] AS [e] +``` + + * Count Children With Filter By Closure Async (line 88) + * T-Sql executed is + +```SQL +SELECT ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId] AND [e0].[ChildInt] = [e].[ParentInt]) +FROM [EfParents] AS [e] +``` + + * Count Children With Filter By External Closure Async (line 111) + * T-Sql executed is + +```SQL +SELECT ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId] AND [e0].[ChildInt] = @i) +FROM [EfParents] AS [e] +``` + + * Count Children With Filter By External Closure2 Async (line 135) + * T-Sql executed is + +```SQL +SELECT ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId] AND [e0].[ChildInt] = @i AND [e0].[EfParentId] = @j) +FROM [EfParents] AS [e] +``` + + * Singleton Count Children With Filter Async (line 157) + * T-Sql executed is + +```SQL +SELECT COUNT(*) +FROM [EfParents] AS [e] +WHERE ( + SELECT COUNT(*) + FROM [EfChildren] AS [e0] + WHERE [e].[EfParentId] = [e0].[EfParentId]) = 2 +``` + + + +### Group: Types +#### [Strings](../TestGroup50Types/Test01Strings.cs): +- Supported + * Concatenate Person Not Handle Null (line 36) + * T-Sql executed is + +```SQL +SELECT [e].[FirstName] + N' ' + COALESCE([e].[MiddleName], N'') + N' ' + [e].[LastName] +FROM [EfPersons] AS [e] +``` + + * Concatenate Person Handle Null (line 57) + * T-Sql executed is + +```SQL +SELECT [e].[FirstName] + CASE + WHEN [e].[MiddleName] IS NULL THEN N'' + ELSE N' ' +END + COALESCE([e].[MiddleName], N'') + N' ' + [e].[LastName] +FROM [EfPersons] AS [e] +``` + + * Concatenate Person Handle Name Order (line 80) + * T-Sql executed is + +```SQL +SELECT CASE + WHEN [e].[NameOrder] = CAST(1 AS bit) THEN [e].[LastName] + N', ' + [e].[FirstName] + CASE + WHEN [e].[MiddleName] IS NULL THEN N'' + ELSE N' ' + END + ELSE [e].[FirstName] + CASE + WHEN [e].[MiddleName] IS NULL THEN N'' + ELSE N' ' + END + COALESCE([e].[MiddleName], N'') + N' ' + [e].[LastName] +END +FROM [EfPersons] AS [e] +``` + + * Select Generic Method Person Handle (line 101) + * T-Sql executed is + +```SQL +SELECT [e].[FirstName] + CASE + WHEN [e].[MiddleName] IS NULL THEN N'' + ELSE N' ' +END + COALESCE([e].[MiddleName], N'') + N' ' + [e].[LastName] +FROM [EfPersons] AS [e] +``` + + * Filter Generic Method Person Handle (line 119) + * T-Sql executed is + +```SQL +SELECT [e].[EfPersonId], [e].[FirstName], [e].[LastName], [e].[MiddleName], [e].[NameOrder] +FROM [EfPersons] AS [e] +``` + + +#### [DateTime](../TestGroup50Types/Test05DateTime.cs): +- Supported + * DateTime Where Compare With Static Variable (line 39) + * T-Sql executed is + +```SQL +SELECT [e].[StartDate] +FROM [EfParents] AS [e] +WHERE [e].[StartDate] > '2000-01-01T00:00:00.0000000' +``` + + + +### Group: Additional Features +#### [Nested Expressions](../TestGroup90AdditionalFeatures/Test01NestedExpressions.cs): +- Supported + * Subquery As Context Extension Method (line 72) + * T-Sql executed is + +```SQL +SELECT [e].[EfParentId] AS [ParentId], COALESCE(( + SELECT TOP(1) [e0].[EfChildId] + FROM [EfChildren] AS [e0] + WHERE [e0].[EfParentId] = [e].[EfParentId]), 0) AS [FirstChildId] +FROM [EfParents] AS [e] +``` + + + + +The End diff --git a/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/DocumentationHeaderText.md b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/DocumentationHeaderText.md new file mode 100644 index 00000000..3a89a85d --- /dev/null +++ b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/DocumentationHeaderText.md @@ -0,0 +1,20 @@ + +This file documents what linq commands **DelegateDecompiler** supports when +working with [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/) (EF). +EF has one of the best implementations for converting Linq `IQueryable<>` commands into database +access commands, in EF's case T-SQL. Therefore it is a good candidate for using in our tests. + +This documentation was produced by compaired direct EF Linq queries against the same query implemented +as a DelegateDecompiler's `Computed` properties. This produces a Supported/Not Supported flag +on each command type tested. Tests are groups and ordered to try and make finding things +easier. + +So, if you want to use DelegateDecompiler and are not sure whether the linq command +you want to use will work then clone this project and write your own tests. +(See [How to add a test](HowToAddMoreTests.md) documentation on how to do this). +If there is a problem then please fork the repository and add your own tests. +That will make it much easier to diagnose your issue. + +*Note: The test suite has only recently been set up and has only a handful of tests at the moment. +More will appear as we move forward.* + diff --git a/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/SummaryOfSupportedCommands.md b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/SummaryOfSupportedCommands.md new file mode 100644 index 00000000..6af3088e --- /dev/null +++ b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/SummaryOfSupportedCommands.md @@ -0,0 +1,62 @@ +Summary of supported commands +============ +## Documentation produced for DelegateDecompiler, version 0.34.3.0 on Wednesday, 05 March 2025 18:24 + +This file documents what linq commands **DelegateDecompiler** supports when +working with [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/) (EF). +EF has one of the best implementations for converting Linq `IQueryable<>` commands into database +access commands, in EF's case T-SQL. Therefore it is a good candidate for using in our tests. + +This documentation was produced by compaired direct EF Linq queries against the same query implemented +as a DelegateDecompiler's `Computed` properties. This produces a Supported/Not Supported flag +on each command type tested. Tests are groups and ordered to try and make finding things +easier. + +So, if you want to use DelegateDecompiler and are not sure whether the linq command +you want to use will work then clone this project and write your own tests. +(See [How to add a test](HowToAddMoreTests.md) documentation on how to do this). +If there is a problem then please fork the repository and add your own tests. +That will make it much easier to diagnose your issue. + +*Note: The test suite has only recently been set up and has only a handful of tests at the moment. +More will appear as we move forward.* + + +### Group: Basic Features +- Supported + * [Select](../TestGroup05BasicFeatures/Test01Select.cs) (11 tests) + * [Select Async](../TestGroup05BasicFeatures/Test02SelectAsync.cs) (5 tests) + * [Equals And Not Equals](../TestGroup05BasicFeatures/Test03EqualsAndNotEquals.cs) (4 tests) + * [Nullable](../TestGroup05BasicFeatures/Test04Nullable.cs) (5 tests) + * [Where](../TestGroup05BasicFeatures/Test05Where.cs) (4 tests) + * [Single](../TestGroup05BasicFeatures/Test10Single.cs) (1 tests) + * [Single Async](../TestGroup05BasicFeatures/Test11SingleAsync.cs) (1 tests) + +### Group: Order Take +- Supported + * [Order By](../TestGroup10OrderTake/Test01OrderBy.cs) (3 tests) + * [Skip Take](../TestGroup10OrderTake/Test02SkipTake.cs) (3 tests) + +### Group: Quantifier Operators +- Supported + * [Any](../TestGroup12QuantifierOperators/Test01Any.cs) (2 tests) + * [All](../TestGroup12QuantifierOperators/Test02All.cs) (2 tests) + * [Contains](../TestGroup12QuantifierOperators/Test03Contains.cs) (1 tests) + +### Group: Aggregation +- Supported + * [Count](../TestGroup15Aggregation/Test01Count.cs) (6 tests) + * [Sum](../TestGroup15Aggregation/Test02Sum.cs) (1 tests) + * [Count Async](../TestGroup15Aggregation/Test03CountAsync.cs) (6 tests) + +### Group: Types +- Supported + * [Strings](../TestGroup50Types/Test01Strings.cs) (5 tests) + * [DateTime](../TestGroup50Types/Test05DateTime.cs) (1 tests) + +### Group: Additional Features +- Supported + * [Nested Expressions](../TestGroup90AdditionalFeatures/Test01NestedExpressions.cs) (1 tests) + + +The End diff --git a/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/Test file - please ignore.txt b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/Test file - please ignore.txt new file mode 100644 index 00000000..875c32fa --- /dev/null +++ b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/GeneratedDocumentation/Test file - please ignore.txt @@ -0,0 +1 @@ +This is a Unit Test of the MarkupFileHelpers \ No newline at end of file diff --git a/test.cmd b/test.cmd index 01758b75..3af85d9b 100644 --- a/test.cmd +++ b/test.cmd @@ -10,5 +10,7 @@ dotnet test --no-build -c Release -f net8.0 src\DelegateDecompiler.Tests.VB && ^ dotnet test --no-build -c Release -f net8.0 src\DelegateDecompiler.EntityFramework.Tests && ^ dotnet test --no-build -c Release -f net8.0 src\DelegateDecompiler.EntityFrameworkCore6.Tests && ^ dotnet test --no-build -c Release -f net8.0 src\DelegateDecompiler.EntityFrameworkCore8.Tests && ^ -dotnet test --no-build -c Release -f net9.0 src\DelegateDecompiler.EntityFrameworkCore9.Tests +dotnet test --no-build -c Release -f net9.0 src\DelegateDecompiler.EntityFrameworkCore9.Tests && ^ +dotnet test --no-build -c Release -f net10.0 src\DelegateDecompiler.EntityFrameworkCore10.Tests + exit /b From 21f3722d7fb99f57afab78e281999478e48a301d Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Wed, 19 Nov 2025 13:10:27 +1000 Subject: [PATCH 02/13] Add support for constrained opcode (#299) +semver:fix --- src/DelegateDecompiler.Tests/Issue298.cs | 37 +++++++++++++++++++ .../Processors/ConvertTypeProcessor.cs | 13 +++++-- 2 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 src/DelegateDecompiler.Tests/Issue298.cs diff --git a/src/DelegateDecompiler.Tests/Issue298.cs b/src/DelegateDecompiler.Tests/Issue298.cs new file mode 100644 index 00000000..ec7b676c --- /dev/null +++ b/src/DelegateDecompiler.Tests/Issue298.cs @@ -0,0 +1,37 @@ +using System; +using System.Linq.Expressions; +using NUnit.Framework; + +namespace DelegateDecompiler.Tests; + +[TestFixture] +public class Issue298 : DecompilerTestsBase +{ + [Test] + public void TestNotConstrained() + { + static int NotConstrained(T value) where T : ITestInterface => ((ITestInterface)value).Value; + + Expression> expected = value => value.Value; + Test(NotConstrained, expected); + } + + [Test] + public void TestConstrained() + { + static int Constrained(T value) where T : ITestInterface => value.Value; + + Expression> expected = value => value.Value; + Test(Constrained, expected); + } + + interface ITestInterface + { + public int Value { get; } + } + + class TestClass : ITestInterface + { + public int Value { get; set; } + } +} diff --git a/src/DelegateDecompiler/Processors/ConvertTypeProcessor.cs b/src/DelegateDecompiler/Processors/ConvertTypeProcessor.cs index b4df2d78..39144fa4 100644 --- a/src/DelegateDecompiler/Processors/ConvertTypeProcessor.cs +++ b/src/DelegateDecompiler/Processors/ConvertTypeProcessor.cs @@ -10,13 +10,20 @@ internal class ConvertTypeProcessor : IProcessor { public static void Register(Dictionary processors) { - processors.Register(new ConvertTypeProcessor(), OpCodes.Castclass, OpCodes.Unbox, OpCodes.Unbox_Any); + processors.Register(new ConvertTypeProcessor(), + OpCodes.Castclass, + OpCodes.Unbox, + OpCodes.Unbox_Any, + OpCodes.Constrained); } - + public void Process(ProcessorState state, Instruction instruction) { var expression = state.Stack.Pop(); - state.Stack.Push(Expression.Convert(expression, (Type)instruction.Operand)); + var targetType = (Type)instruction.Operand; + state.Stack.Push(expression.Type != targetType + ? Expression.Convert(expression, targetType) + : expression); } } } From 190a81f7974cf3974e568dc1cac13d0d718e037b Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Wed, 19 Nov 2025 13:19:22 +1000 Subject: [PATCH 03/13] Use Trusted Publishing for NuGet (#300) +semver:patch --- .github/workflows/publish-nuget.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml index e22ad5e1..e8533c31 100644 --- a/.github/workflows/publish-nuget.yml +++ b/.github/workflows/publish-nuget.yml @@ -30,6 +30,4 @@ jobs: run: dotnet pack --no-build --configuration Release --output ./Release - name: Publish to NuGet - run: dotnet nuget push ./Release/*.nupkg -s nuget.org -k ${NUGET_API_KEY} - env: - NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} + run: dotnet nuget push ./Release/*.nupkg -s nuget.org From 334a572a6506ff6986c2a3337b6ba179dcd97131 Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Wed, 19 Nov 2025 13:36:54 +1000 Subject: [PATCH 04/13] Fix Trusted Publishing (#301) +semver:patch --- .github/workflows/publish-nuget.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml index e8533c31..26e92356 100644 --- a/.github/workflows/publish-nuget.yml +++ b/.github/workflows/publish-nuget.yml @@ -29,5 +29,10 @@ jobs: - name: Pack run: dotnet pack --no-build --configuration Release --output ./Release - - name: Publish to NuGet - run: dotnet nuget push ./Release/*.nupkg -s nuget.org + - name: NuGet Login + uses: NuGet/login@v1 + with: + user: hazzik + + - name: NuGet Publish + run: dotnet nuget push ./Release/*.nupkg -s nuget.org -k ${{ steps.login.outputs.NUGET_API_KEY }} From 25c70995f1566fb74cf2f5deb3afd7c162c948b9 Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Wed, 19 Nov 2025 13:44:13 +1000 Subject: [PATCH 05/13] Automate release creation (#302) +semver:patch --- .../{create-release-tag.yml => create-release.yml} | 8 +++----- .github/workflows/publish-nuget.yml | 4 ++++ 2 files changed, 7 insertions(+), 5 deletions(-) rename .github/workflows/{create-release-tag.yml => create-release.yml} (66%) diff --git a/.github/workflows/create-release-tag.yml b/.github/workflows/create-release.yml similarity index 66% rename from .github/workflows/create-release-tag.yml rename to .github/workflows/create-release.yml index 24ff261c..97c86071 100644 --- a/.github/workflows/create-release-tag.yml +++ b/.github/workflows/create-release.yml @@ -1,4 +1,4 @@ -name: Create Release Tag +name: Create Release on: workflow_dispatch: @@ -21,7 +21,5 @@ jobs: id: gitversion uses: gittools/actions/gitversion/execute@v4 - - name: Create and Push Tag - run: | - git tag ${{ steps.gitversion.outputs.majorMinorPatch }} - git push origin ${{ steps.gitversion.outputs.majorMinorPatch }} + - name: Create Release + run: gh release create ${{ steps.gitversion.outputs.majorMinorPatch }} --generate-notes diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml index 26e92356..18080636 100644 --- a/.github/workflows/publish-nuget.yml +++ b/.github/workflows/publish-nuget.yml @@ -5,6 +5,10 @@ on: tags: - '*' +permissions: + id-token: write + contents: read + jobs: publish: runs-on: ubuntu-latest From 295d941f4475e53ad2662690cdd847bf01bb2353 Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Wed, 19 Nov 2025 13:46:28 +1000 Subject: [PATCH 06/13] Fix workflows - Add missing GH_TOKEN env variable to Create Release step - Add missing step id to NuGet Login step +semver:fix --- .github/workflows/create-release.yml | 2 ++ .github/workflows/publish-nuget.yml | 1 + 2 files changed, 3 insertions(+) diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 97c86071..67d7ab97 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -23,3 +23,5 @@ jobs: - name: Create Release run: gh release create ${{ steps.gitversion.outputs.majorMinorPatch }} --generate-notes + env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml index 18080636..45cb51bc 100644 --- a/.github/workflows/publish-nuget.yml +++ b/.github/workflows/publish-nuget.yml @@ -35,6 +35,7 @@ jobs: - name: NuGet Login uses: NuGet/login@v1 + id: login with: user: hazzik From fc8446289d304ddba31cd036875a7932a8fe8405 Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Wed, 19 Nov 2025 19:23:03 +1000 Subject: [PATCH 07/13] Fix support for prefix opcodes (#304) Fixes #303 +semver:fix --- src/DelegateDecompiler.Tests/Issue298.cs | 36 ++++++++++++++----- src/DelegateDecompiler/Processor.cs | 3 +- .../Processors/ConvertTypeProcessor.cs | 12 +++---- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/DelegateDecompiler.Tests/Issue298.cs b/src/DelegateDecompiler.Tests/Issue298.cs index ec7b676c..9c5ad1c8 100644 --- a/src/DelegateDecompiler.Tests/Issue298.cs +++ b/src/DelegateDecompiler.Tests/Issue298.cs @@ -10,28 +10,46 @@ public class Issue298 : DecompilerTestsBase [Test] public void TestNotConstrained() { - static int NotConstrained(T value) where T : ITestInterface => ((ITestInterface)value).Value; - - Expression> expected = value => value.Value; - Test(NotConstrained, expected); + static int Actual(T value) where T : ITestInterface => ((ITestInterface)value).Field; + + Test(Actual, + value => value.Field, + value => ((ITestInterface)value).Field + ); } [Test] public void TestConstrained() { - static int Constrained(T value) where T : ITestInterface => value.Value; + static int Actual(T value) where T : ITestInterface => value.Field; - Expression> expected = value => value.Value; - Test(Constrained, expected); + Test(Actual, value => value.Field); + Test(Actual, value => value.Field); } +#if NET6_0_OR_GREATER + [Test] + public void TestConstrainedDefaultImplementation() + { + static int Actual(T value, int i) where T : ITestInterface => value.Method(i); + static Expression> Expected() where T : ITestInterface => (value, i) => value.Method(i); + + Test(Actual, Expected()); + Test(Actual, Expected()); + } +#endif + interface ITestInterface { - public int Value { get; } + public int Field { get; } + +#if NET6_0_OR_GREATER + public int Method(int i) => i; +#endif } class TestClass : ITestInterface { - public int Value { get; set; } + public int Field { get; set; } } } diff --git a/src/DelegateDecompiler/Processor.cs b/src/DelegateDecompiler/Processor.cs index c1c8b8b1..aa5271ca 100644 --- a/src/DelegateDecompiler/Processor.cs +++ b/src/DelegateDecompiler/Processor.cs @@ -145,9 +145,10 @@ static int ProcessInstruction(ProcessorState state, Instruction instruction) { Debug.WriteLine(instruction); - if (instruction.OpCode == OpCodes.Nop || + if (instruction.OpCode == OpCodes.Nop || instruction.OpCode == OpCodes.Break || instruction.OpCode == OpCodes.Ret || + instruction.OpCode.OpCodeType == OpCodeType.Prefix || instruction.OpCode.FlowControl == FlowControl.Branch) { // Do nothing diff --git a/src/DelegateDecompiler/Processors/ConvertTypeProcessor.cs b/src/DelegateDecompiler/Processors/ConvertTypeProcessor.cs index 39144fa4..043907ca 100644 --- a/src/DelegateDecompiler/Processors/ConvertTypeProcessor.cs +++ b/src/DelegateDecompiler/Processors/ConvertTypeProcessor.cs @@ -10,20 +10,16 @@ internal class ConvertTypeProcessor : IProcessor { public static void Register(Dictionary processors) { - processors.Register(new ConvertTypeProcessor(), - OpCodes.Castclass, + processors.Register(new ConvertTypeProcessor(), + OpCodes.Castclass, OpCodes.Unbox, - OpCodes.Unbox_Any, - OpCodes.Constrained); + OpCodes.Unbox_Any); } public void Process(ProcessorState state, Instruction instruction) { var expression = state.Stack.Pop(); - var targetType = (Type)instruction.Operand; - state.Stack.Push(expression.Type != targetType - ? Expression.Convert(expression, targetType) - : expression); + state.Stack.Push(Expression.Convert(expression, (Type)instruction.Operand)); } } } From 2e233a03bf122d38e3ca065729a74d568b2201a3 Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Wed, 19 Nov 2025 19:35:36 +1000 Subject: [PATCH 08/13] Remove EntityFrameworkCore6 tests (#305) +semver:patch --- .github/workflows/dotnet-linux.yml | 1 - .github/workflows/dotnet-windows.yml | 1 - DelegateDecompiler.sln | 6 - ...compiler.EntityFrameworkCore6.Tests.csproj | 45 - .../DetailedListOfSupportedCommands.md | 154 ---- .../DetailedListOfSupportedCommandsWithSQL.md | 809 ------------------ .../DocumentationHeaderText.md | 20 - .../SummaryOfSupportedCommands.md | 62 -- .../Test file - please ignore.txt | 1 - test.cmd | 1 - 10 files changed, 1100 deletions(-) delete mode 100644 src/DelegateDecompiler.EntityFrameworkCore6.Tests/DelegateDecompiler.EntityFrameworkCore6.Tests.csproj delete mode 100644 src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/DetailedListOfSupportedCommands.md delete mode 100644 src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/DetailedListOfSupportedCommandsWithSQL.md delete mode 100644 src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/DocumentationHeaderText.md delete mode 100644 src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/SummaryOfSupportedCommands.md delete mode 100644 src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/Test file - please ignore.txt diff --git a/.github/workflows/dotnet-linux.yml b/.github/workflows/dotnet-linux.yml index cfa433d3..d9e4646e 100644 --- a/.github/workflows/dotnet-linux.yml +++ b/.github/workflows/dotnet-linux.yml @@ -58,7 +58,6 @@ jobs: dotnet test --no-build -c Release -f net8.0 src/DelegateDecompiler.Tests dotnet test --no-build -c Release -f net8.0 src/DelegateDecompiler.Tests.VB dotnet test --no-build -c Release -f net8.0 src/DelegateDecompiler.EntityFramework.Tests - dotnet test --no-build -c Release -f net8.0 src/DelegateDecompiler.EntityFrameworkCore6.Tests dotnet test --no-build -c Release -f net8.0 src/DelegateDecompiler.EntityFrameworkCore8.Tests dotnet test --no-build -c Release -f net9.0 src/DelegateDecompiler.EntityFrameworkCore9.Tests dotnet test --no-build -c Release -f net10.0 src\DelegateDecompiler.EntityFrameworkCore10.Tests diff --git a/.github/workflows/dotnet-windows.yml b/.github/workflows/dotnet-windows.yml index f9988079..69120ff8 100644 --- a/.github/workflows/dotnet-windows.yml +++ b/.github/workflows/dotnet-windows.yml @@ -52,7 +52,6 @@ jobs: dotnet test --no-build -c Release -f net8.0 src/DelegateDecompiler.Tests dotnet test --no-build -c Release -f net8.0 src/DelegateDecompiler.Tests.VB dotnet test --no-build -c Release -f net8.0 src/DelegateDecompiler.EntityFramework.Tests - dotnet test --no-build -c Release -f net8.0 src/DelegateDecompiler.EntityFrameworkCore6.Tests dotnet test --no-build -c Release -f net8.0 src/DelegateDecompiler.EntityFrameworkCore8.Tests dotnet test --no-build -c Release -f net9.0 src/DelegateDecompiler.EntityFrameworkCore9.Tests dotnet test --no-build -c Release -f net10.0 src\DelegateDecompiler.EntityFrameworkCore10.Tests diff --git a/DelegateDecompiler.sln b/DelegateDecompiler.sln index 285f7be7..ebebdfdf 100644 --- a/DelegateDecompiler.sln +++ b/DelegateDecompiler.sln @@ -27,8 +27,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DelegateDecompiler.EntityFr EndProject Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "DelegateDecompiler.Tests.VB", "src\DelegateDecompiler.Tests.VB\DelegateDecompiler.Tests.VB.vbproj", "{19EC81F1-783D-4F53-BCB8-4F87BA3A28EC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DelegateDecompiler.EntityFrameworkCore6.Tests", "src\DelegateDecompiler.EntityFrameworkCore6.Tests\DelegateDecompiler.EntityFrameworkCore6.Tests.csproj", "{0EC42D3F-C97B-4803-96BF-953B0139578C}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DelegateDecompiler.EntityFrameworkCore5", "src\DelegateDecompiler.EntityFrameworkCore5\DelegateDecompiler.EntityFrameworkCore5.csproj", "{79DA2FBB-7A35-4AFF-A89B-CF8F5DADC1B2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DelegateDecompiler.EntityFrameworkCore8.Tests", "src\DelegateDecompiler.EntityFrameworkCore8.Tests\DelegateDecompiler.EntityFrameworkCore8.Tests.csproj", "{4B729DD4-7A37-4D16-B241-10DD5B12EDA0}" @@ -63,10 +61,6 @@ Global {19EC81F1-783D-4F53-BCB8-4F87BA3A28EC}.Debug|Any CPU.Build.0 = Debug|Any CPU {19EC81F1-783D-4F53-BCB8-4F87BA3A28EC}.Release|Any CPU.ActiveCfg = Release|Any CPU {19EC81F1-783D-4F53-BCB8-4F87BA3A28EC}.Release|Any CPU.Build.0 = Release|Any CPU - {0EC42D3F-C97B-4803-96BF-953B0139578C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0EC42D3F-C97B-4803-96BF-953B0139578C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0EC42D3F-C97B-4803-96BF-953B0139578C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0EC42D3F-C97B-4803-96BF-953B0139578C}.Release|Any CPU.Build.0 = Release|Any CPU {79DA2FBB-7A35-4AFF-A89B-CF8F5DADC1B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {79DA2FBB-7A35-4AFF-A89B-CF8F5DADC1B2}.Debug|Any CPU.Build.0 = Debug|Any CPU {79DA2FBB-7A35-4AFF-A89B-CF8F5DADC1B2}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/DelegateDecompiler.EntityFrameworkCore6.Tests/DelegateDecompiler.EntityFrameworkCore6.Tests.csproj b/src/DelegateDecompiler.EntityFrameworkCore6.Tests/DelegateDecompiler.EntityFrameworkCore6.Tests.csproj deleted file mode 100644 index df184a01..00000000 --- a/src/DelegateDecompiler.EntityFrameworkCore6.Tests/DelegateDecompiler.EntityFrameworkCore6.Tests.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - net8.0 - Copyright © Dave Glick 2014, Jon Smith 2014, Alexander Zaytsev 2014 - 2025 - $(DefineConstants);EF_CORE;EF_CORE3;EF_CORE5 - DelegateDecompiler.EntityFramework.Tests - - - - - - %(Name) - - - - - - %(Name) - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/DetailedListOfSupportedCommands.md b/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/DetailedListOfSupportedCommands.md deleted file mode 100644 index 064a71d4..00000000 --- a/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/DetailedListOfSupportedCommands.md +++ /dev/null @@ -1,154 +0,0 @@ -Detail of supported commands -============ -## Documentation produced for DelegateDecompiler, version 0.34.2.0 on Wednesday, 05 March 2025 13:06 - -This file documents what linq commands **DelegateDecompiler** supports when -working with [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/) (EF). -EF has one of the best implementations for converting Linq `IQueryable<>` commands into database -access commands, in EF's case T-SQL. Therefore it is a good candidate for using in our tests. - -This documentation was produced by compaired direct EF Linq queries against the same query implemented -as a DelegateDecompiler's `Computed` properties. This produces a Supported/Not Supported flag -on each command type tested. Tests are groups and ordered to try and make finding things -easier. - -So, if you want to use DelegateDecompiler and are not sure whether the linq command -you want to use will work then clone this project and write your own tests. -(See [How to add a test](HowToAddMoreTests.md) documentation on how to do this). -If there is a problem then please fork the repository and add your own tests. -That will make it much easier to diagnose your issue. - -*Note: The test suite has only recently been set up and has only a handful of tests at the moment. -More will appear as we move forward.* - - -### Group: Basic Features -#### [Select](../TestGroup05BasicFeatures/Test01Select.cs): -- Supported - * Bool Equals Constant (line 38) - * Bool Equals Static Variable (line 61) - * Int Equals Constant (line 82) - * Select Property Without Computed Attribute (line 103) - * Select Method Without Computed Attribute (line 124) - * Select Abstract Member Over Tph Hierarchy (line 145) - * Select Abstract Member Over Tph Hierarchy After Restricting To Subtype (line 166) - * Select Abstract Member Over Tph Hierarchy With Generic Classes After Restricting To Subtype (line 184) - * Select Abstract Member With Condition On It Over Tph Hierarchy With Generic Classes After Restricting To Subtype (line 212) - * Select Multiple Levels Of Abstract Members Over Tph Hierarchy (line 234) - * Select Select Many (line 256) - -#### [Select Async](../TestGroup05BasicFeatures/Test02SelectAsync.cs): -- Supported - * Async (line 43) - * Bool Equals Constant Async (line 83) - * Decompile Upfront Bool Equals Constant Async (line 104) - * Bool Equals Static Variable To Array Async (line 127) - * Int Equals Constant (line 148) - -#### [Equals And Not Equals](../TestGroup05BasicFeatures/Test03EqualsAndNotEquals.cs): -- Supported - * Int Equals Constant (line 36) - * Int Equals Static Variable (line 58) - * Int Equals String Length (line 79) - * Int Not Equals String Length (line 100) - -#### [Nullable](../TestGroup05BasicFeatures/Test04Nullable.cs): -- Supported - * Property Is Null (line 39) - * Bool Equals Static Variable (line 62) - * Int Equals Constant (line 83) - * Nullable Init (line 104) - * Nullable Add (line 125) - -#### [Where](../TestGroup05BasicFeatures/Test05Where.cs): -- Supported - * Where Bool Equals Constant (line 37) - * Where Bool Equals Static Variable (line 60) - * Where Int Equals Constant (line 81) - * Where Filters On Abstract Members Over Tph Hierarchy (line 102) - -#### [Single](../TestGroup05BasicFeatures/Test10Single.cs): -- Supported - * Single Int Equals Unique Value (line 42) - -#### [Single Async](../TestGroup05BasicFeatures/Test11SingleAsync.cs): -- Supported - * Single Int Equals Unique Value Async (line 48) - - -### Group: Order Take -#### [Order By](../TestGroup10OrderTake/Test01OrderBy.cs): -- Supported - * Order By Children Count (line 37) - * Order By Children Count Then By String Length (line 59) - * Where Any Children Then Order By Children Count (line 81) - -#### [Skip Take](../TestGroup10OrderTake/Test02SkipTake.cs): -- Supported - * Order By Children Count Then Take (line 37) - * Order By Children Count Then Skip And Take (line 59) - * Where Any Children Then Order By Then Skip Take (line 81) - - -### Group: Quantifier Operators -#### [Any](../TestGroup12QuantifierOperators/Test01Any.cs): -- Supported - * Any Children (line 36) - * Any Children With Filter (line 57) - -#### [All](../TestGroup12QuantifierOperators/Test02All.cs): -- Supported - * Singleton All Filter (line 36) - * All Filter On Children Int (line 57) - -#### [Contains](../TestGroup12QuantifierOperators/Test03Contains.cs): -- Supported - * String Contains Constant String With Filter (line 37) - - -### Group: Aggregation -#### [Count](../TestGroup15Aggregation/Test01Count.cs): -- Supported - * Count Children (line 37) - * Count Children With Filter (line 59) - * Count Children With Filter By Closure (line 81) - * Count Children With Filter By External Closure (line 104) - * Count Children With Filter By External Closure2 (line 128) - * Singleton Count Children With Filter (line 150) - -#### [Sum](../TestGroup15Aggregation/Test02Sum.cs): -- Supported - * Sum Count In Children Where Children Can Be None (line 61) - -#### [Count Async](../TestGroup15Aggregation/Test03CountAsync.cs): -- Supported - * Count Children Async (line 44) - * Count Children With Filter Async (line 66) - * Count Children With Filter By Closure Async (line 88) - * Count Children With Filter By External Closure Async (line 111) - * Count Children With Filter By External Closure2 Async (line 135) - * Singleton Count Children With Filter Async (line 157) - - -### Group: Types -#### [Strings](../TestGroup50Types/Test01Strings.cs): -- Supported - * Concatenate Person Not Handle Null (line 36) - * Concatenate Person Handle Null (line 57) - * Concatenate Person Handle Name Order (line 80) - * Select Generic Method Person Handle (line 101) - * Filter Generic Method Person Handle (line 119) - -#### [DateTime](../TestGroup50Types/Test05DateTime.cs): -- Supported - * DateTime Where Compare With Static Variable (line 39) - - -### Group: Additional Features -#### [Nested Expressions](../TestGroup90AdditionalFeatures/Test01NestedExpressions.cs): -- Supported - * Subquery As Context Extension Method (line 72) - - - -The End diff --git a/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/DetailedListOfSupportedCommandsWithSQL.md b/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/DetailedListOfSupportedCommandsWithSQL.md deleted file mode 100644 index 6556dc70..00000000 --- a/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/DetailedListOfSupportedCommandsWithSQL.md +++ /dev/null @@ -1,809 +0,0 @@ -Detail With Sql of supported commands -============ -## Documentation produced for DelegateDecompiler, version 0.34.2.0 on Wednesday, 05 March 2025 13:06 - -This file documents what linq commands **DelegateDecompiler** supports when -working with [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/) (EF). -EF has one of the best implementations for converting Linq `IQueryable<>` commands into database -access commands, in EF's case T-SQL. Therefore it is a good candidate for using in our tests. - -This documentation was produced by compaired direct EF Linq queries against the same query implemented -as a DelegateDecompiler's `Computed` properties. This produces a Supported/Not Supported flag -on each command type tested. Tests are groups and ordered to try and make finding things -easier. - -So, if you want to use DelegateDecompiler and are not sure whether the linq command -you want to use will work then clone this project and write your own tests. -(See [How to add a test](HowToAddMoreTests.md) documentation on how to do this). -If there is a problem then please fork the repository and add your own tests. -That will make it much easier to diagnose your issue. - -*Note: The test suite has only recently been set up and has only a handful of tests at the moment. -More will appear as we move forward.* - - -### Group: Basic Features -#### [Select](../TestGroup05BasicFeatures/Test01Select.cs): -- Supported - * Bool Equals Constant (line 38) - * T-Sql executed is - -```SQL -SELECT [e].[ParentBool] -FROM [EfParents] AS [e] -``` - - * Bool Equals Static Variable (line 61) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN [e].[ParentBool] = @__staticBool_0 THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -FROM [EfParents] AS [e] -``` - - * Int Equals Constant (line 82) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN [e].[ParentInt] = 123 THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -FROM [EfParents] AS [e] -``` - - * Select Property Without Computed Attribute (line 103) - * T-Sql executed is - -```SQL -SELECT ((([e].[FirstName] + N' ') + COALESCE([e].[MiddleName], N'')) + N' ') + [e].[LastName] -FROM [EfPersons] AS [e] -``` - - * Select Method Without Computed Attribute (line 124) - * T-Sql executed is - -```SQL -SELECT ((([e].[FirstName] + N' ') + COALESCE([e].[MiddleName], N'')) + N' ') + [e].[LastName] -FROM [EfPersons] AS [e] -``` - - * Select Abstract Member Over Tph Hierarchy (line 145) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN [l].[Discriminator] = N'Person' THEN N'Human' - WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Carcharodon carcharias' - WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Gadus morhua' - WHEN [l].[Discriminator] = N'HoneyBee' THEN N'Apis mellifera' - WHEN [l].[Discriminator] = N'Dog' THEN N'Canis lupus' - ELSE NULL -END -FROM [LivingBeeing] AS [l] -``` - - * Select Abstract Member Over Tph Hierarchy After Restricting To Subtype (line 166) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN [l].[Discriminator] = N'HoneyBee' THEN N'Apis mellifera' - WHEN [l].[Discriminator] = N'Dog' THEN N'Canis lupus' - ELSE NULL -END -FROM [LivingBeeing] AS [l] -WHERE [l].[Discriminator] IN (N'Dog', N'HoneyBee') -``` - - * Select Abstract Member Over Tph Hierarchy With Generic Classes After Restricting To Subtype (line 184) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Carcharodon carcharias' - WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Gadus morhua' - ELSE NULL -END AS [Species], CASE - WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Fish' - WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Fish' - ELSE NULL -END AS [Group] -FROM [LivingBeeing] AS [l] -WHERE [l].[Discriminator] IN (N'AtlanticCod', N'WhiteShark') -``` - - * Select Abstract Member With Condition On It Over Tph Hierarchy With Generic Classes After Restricting To Subtype (line 212) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Carcharodon carcharias' - WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Gadus morhua' - ELSE NULL -END AS [Species], CASE - WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Fish' - WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Fish' - ELSE NULL -END AS [Group] -FROM [LivingBeeing] AS [l] -WHERE [l].[Discriminator] IN (N'AtlanticCod', N'WhiteShark') AND ((CASE - WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Carcharodon carcharias' - WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Gadus morhua' - ELSE NULL -END IS NOT NULL) AND (CASE - WHEN [l].[Discriminator] = N'WhiteShark' THEN N'Fish' - WHEN [l].[Discriminator] = N'AtlanticCod' THEN N'Fish' - ELSE NULL -END IS NOT NULL)) -``` - - * Select Multiple Levels Of Abstract Members Over Tph Hierarchy (line 234) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN [l].[Discriminator] = N'HoneyBee' THEN N'Apis mellifera' - WHEN [l].[Discriminator] = N'Dog' THEN N'Canis lupus' - ELSE NULL -END, CASE - WHEN [l].[Discriminator] = N'HoneyBee' THEN CAST(0 AS bit) - ELSE CASE - WHEN [l].[Discriminator] = N'Dog' THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END -END -FROM [LivingBeeing] AS [l] -WHERE [l].[Discriminator] IN (N'Dog', N'HoneyBee') -``` - - * Select Select Many (line 256) - * T-Sql executed is - -```SQL -SELECT [t0].[EfGrandChildId], [t0].[EfChildId], [t0].[GrandChildBool], [t0].[GrandChildDouble], [t0].[GrandChildInt], [t0].[GrandChildString] -FROM [EfParents] AS [e] -LEFT JOIN ( - SELECT [t].[EfGrandChildId], [t].[EfChildId], [t].[GrandChildBool], [t].[GrandChildDouble], [t].[GrandChildInt], [t].[GrandChildString], [t].[EfParentId] - FROM ( - SELECT [e1].[EfGrandChildId], [e1].[EfChildId], [e1].[GrandChildBool], [e1].[GrandChildDouble], [e1].[GrandChildInt], [e1].[GrandChildString], [e0].[EfParentId], ROW_NUMBER() OVER(PARTITION BY [e0].[EfParentId] ORDER BY [e1].[EfGrandChildId]) AS [row] - FROM [EfChildren] AS [e0] - INNER JOIN [EfGrandChildren] AS [e1] ON [e0].[EfChildId] = [e1].[EfChildId] - ) AS [t] - WHERE [t].[row] <= 1 -) AS [t0] ON [e].[EfParentId] = [t0].[EfParentId] -``` - - -#### [Select Async](../TestGroup05BasicFeatures/Test02SelectAsync.cs): -- Supported - * Async (line 43) - * T-Sql executed is - -```SQL -SELECT [e].[EfParentId], [e].[EndDate], [e].[ParentBool], [e].[ParentDouble], [e].[ParentInt], [e].[ParentNullableDecimal1], [e].[ParentNullableDecimal2], [e].[ParentNullableInt], [e].[ParentString], [e].[ParentTimeSpan], [e].[StartDate] -FROM [EfParents] AS [e] -``` - - * Bool Equals Constant Async (line 83) - * T-Sql executed is - -```SQL -SELECT [e].[ParentBool] -FROM [EfParents] AS [e] -``` - - * Decompile Upfront Bool Equals Constant Async (line 104) - * T-Sql executed is - -```SQL -SELECT [e].[ParentBool] -FROM [EfParents] AS [e] -``` - - * Bool Equals Static Variable To Array Async (line 127) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN [e].[ParentBool] = @__staticBool_0 THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -FROM [EfParents] AS [e] -``` - - * Int Equals Constant (line 148) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN [e].[ParentInt] = 123 THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -FROM [EfParents] AS [e] -``` - - -#### [Equals And Not Equals](../TestGroup05BasicFeatures/Test03EqualsAndNotEquals.cs): -- Supported - * Int Equals Constant (line 36) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN [e].[ParentInt] = 123 THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -FROM [EfParents] AS [e] -``` - - * Int Equals Static Variable (line 58) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN [e].[ParentInt] = @__staticInt_0 THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -FROM [EfParents] AS [e] -``` - - * Int Equals String Length (line 79) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN ([e].[ParentInt] = CAST(LEN([e].[ParentString]) AS int)) AND ([e].[ParentString] IS NOT NULL) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -FROM [EfParents] AS [e] -``` - - * Int Not Equals String Length (line 100) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN ([e].[ParentInt] <> CAST(LEN([e].[ParentString]) AS int)) OR ([e].[ParentString] IS NULL) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -FROM [EfParents] AS [e] -``` - - -#### [Nullable](../TestGroup05BasicFeatures/Test04Nullable.cs): -- Supported - * Property Is Null (line 39) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN [e].[ParentNullableInt] IS NULL THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -FROM [EfParents] AS [e] -``` - - * Bool Equals Static Variable (line 62) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN [e].[ParentNullableInt] IS NULL THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -FROM [EfParents] AS [e] -``` - - * Int Equals Constant (line 83) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN COALESCE([e].[ParentNullableInt], 0) = 123 THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -FROM [EfParents] AS [e] -``` - - * Nullable Init (line 104) - * T-Sql executed is - -```SQL -SELECT NULL -FROM [EfParents] AS [e] -``` - - * Nullable Add (line 125) - * T-Sql executed is - -```SQL -SELECT [e].[ParentNullableDecimal1] + [e].[ParentNullableDecimal2] -FROM [EfParents] AS [e] -``` - - -#### [Where](../TestGroup05BasicFeatures/Test05Where.cs): -- Supported - * Where Bool Equals Constant (line 37) - * T-Sql executed is - -```SQL -SELECT [e].[EfParentId] -FROM [EfParents] AS [e] -WHERE [e].[ParentBool] = CAST(1 AS bit) -``` - - * Where Bool Equals Static Variable (line 60) - * T-Sql executed is - -```SQL -SELECT [e].[EfParentId] -FROM [EfParents] AS [e] -WHERE [e].[ParentBool] = @__staticBool_0 -``` - - * Where Int Equals Constant (line 81) - * T-Sql executed is - -```SQL -SELECT [e].[EfParentId] -FROM [EfParents] AS [e] -WHERE [e].[ParentInt] = 123 -``` - - * Where Filters On Abstract Members Over Tph Hierarchy (line 102) - * T-Sql executed is - -```SQL -SELECT [l].[Id] -FROM [LivingBeeing] AS [l] -WHERE [l].[Discriminator] = N'Person' -``` - - -#### [Single](../TestGroup05BasicFeatures/Test10Single.cs): -- Supported - * Single Int Equals Unique Value (line 42) - * T-Sql executed is - -```SQL -SELECT TOP(2) [e].[EfParentId], CASE - WHEN [e].[ParentInt] = 987 THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END AS [IntEqualsUniqueValue] -FROM [EfParents] AS [e] -WHERE [e].[ParentInt] = 987 -``` - - -#### [Single Async](../TestGroup05BasicFeatures/Test11SingleAsync.cs): -- Supported - * Single Int Equals Unique Value Async (line 48) - * T-Sql executed is - -```SQL -SELECT TOP(2) [e].[EfParentId], CASE - WHEN [e].[ParentInt] = 987 THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END AS [IntEqualsUniqueValue] -FROM [EfParents] AS [e] -WHERE [e].[ParentInt] = 987 -``` - - - -### Group: Order Take -#### [Order By](../TestGroup10OrderTake/Test01OrderBy.cs): -- Supported - * Order By Children Count (line 37) - * T-Sql executed is - -```SQL -SELECT [e].[EfParentId] -FROM [EfParents] AS [e] -ORDER BY ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE [e].[EfParentId] = [e0].[EfParentId]) -``` - - * Order By Children Count Then By String Length (line 59) - * T-Sql executed is - -```SQL -SELECT [e].[EfParentId] -FROM [EfParents] AS [e] -ORDER BY ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE [e].[EfParentId] = [e0].[EfParentId]), CAST(LEN([e].[ParentString]) AS int) -``` - - * Where Any Children Then Order By Children Count (line 81) - * T-Sql executed is - -```SQL -SELECT [e].[EfParentId] -FROM [EfParents] AS [e] -WHERE EXISTS ( - SELECT 1 - FROM [EfChildren] AS [e0] - WHERE [e].[EfParentId] = [e0].[EfParentId]) -ORDER BY ( - SELECT COUNT(*) - FROM [EfChildren] AS [e1] - WHERE [e].[EfParentId] = [e1].[EfParentId]) -``` - - -#### [Skip Take](../TestGroup10OrderTake/Test02SkipTake.cs): -- Supported - * Order By Children Count Then Take (line 37) - * T-Sql executed is - -```SQL -SELECT TOP(@__p_0) [e].[EfParentId] -FROM [EfParents] AS [e] -ORDER BY ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE [e].[EfParentId] = [e0].[EfParentId]) -``` - - * Order By Children Count Then Skip And Take (line 59) - * T-Sql executed is - -```SQL -SELECT [e].[EfParentId] -FROM [EfParents] AS [e] -ORDER BY ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE [e].[EfParentId] = [e0].[EfParentId]) -OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY -``` - - * Where Any Children Then Order By Then Skip Take (line 81) - * T-Sql executed is - -```SQL -SELECT [e].[EfParentId] -FROM [EfParents] AS [e] -WHERE EXISTS ( - SELECT 1 - FROM [EfChildren] AS [e0] - WHERE [e].[EfParentId] = [e0].[EfParentId]) -ORDER BY ( - SELECT COUNT(*) - FROM [EfChildren] AS [e1] - WHERE [e].[EfParentId] = [e1].[EfParentId]) -OFFSET @__p_0 ROWS FETCH NEXT @__p_0 ROWS ONLY -``` - - - -### Group: Quantifier Operators -#### [Any](../TestGroup12QuantifierOperators/Test01Any.cs): -- Supported - * Any Children (line 36) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN EXISTS ( - SELECT 1 - FROM [EfChildren] AS [e0] - WHERE [e].[EfParentId] = [e0].[EfParentId]) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -FROM [EfParents] AS [e] -``` - - * Any Children With Filter (line 57) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN EXISTS ( - SELECT 1 - FROM [EfChildren] AS [e0] - WHERE ([e].[EfParentId] = [e0].[EfParentId]) AND ([e0].[ChildInt] = 123)) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -FROM [EfParents] AS [e] -``` - - -#### [All](../TestGroup12QuantifierOperators/Test02All.cs): -- Supported - * Singleton All Filter (line 36) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN NOT EXISTS ( - SELECT 1 - FROM [EfParents] AS [e] - WHERE [e].[ParentInt] <> 123) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -``` - - * All Filter On Children Int (line 57) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN NOT EXISTS ( - SELECT 1 - FROM [EfChildren] AS [e0] - WHERE ([e].[EfParentId] = [e0].[EfParentId]) AND ([e0].[ChildInt] <> 123)) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END -FROM [EfParents] AS [e] -``` - - -#### [Contains](../TestGroup12QuantifierOperators/Test03Contains.cs): -- Supported - * String Contains Constant String With Filter (line 37) - * T-Sql executed is - -```SQL -SELECT [e].[ParentString] -FROM [EfParents] AS [e] -WHERE [e].[ParentString] LIKE N'%2%' -``` - - - -### Group: Aggregation -#### [Count](../TestGroup15Aggregation/Test01Count.cs): -- Supported - * Count Children (line 37) - * T-Sql executed is - -```SQL -SELECT ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE [e].[EfParentId] = [e0].[EfParentId]) -FROM [EfParents] AS [e] -``` - - * Count Children With Filter (line 59) - * T-Sql executed is - -```SQL -SELECT ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE ([e].[EfParentId] = [e0].[EfParentId]) AND ([e0].[ChildInt] = 123)) -FROM [EfParents] AS [e] -``` - - * Count Children With Filter By Closure (line 81) - * T-Sql executed is - -```SQL -SELECT ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE ([e].[EfParentId] = [e0].[EfParentId]) AND ([e0].[ChildInt] = [e].[ParentInt])) -FROM [EfParents] AS [e] -``` - - * Count Children With Filter By External Closure (line 104) - * T-Sql executed is - -```SQL -SELECT ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE ([e].[EfParentId] = [e0].[EfParentId]) AND ([e0].[ChildInt] = @__i_0)) -FROM [EfParents] AS [e] -``` - - * Count Children With Filter By External Closure2 (line 128) - * T-Sql executed is - -```SQL -SELECT ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE ([e].[EfParentId] = [e0].[EfParentId]) AND (([e0].[ChildInt] = @__i_0) AND ([e0].[EfParentId] = @__j_1))) -FROM [EfParents] AS [e] -``` - - * Singleton Count Children With Filter (line 150) - * T-Sql executed is - -```SQL -SELECT COUNT(*) -FROM [EfParents] AS [e] -WHERE ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE [e].[EfParentId] = [e0].[EfParentId]) = 2 -``` - - -#### [Sum](../TestGroup15Aggregation/Test02Sum.cs): -- Supported - * Sum Count In Children Where Children Can Be None (line 61) - * T-Sql executed is - -```SQL -SELECT COALESCE(( - SELECT COALESCE(SUM([e0].[ChildInt]), 0) - FROM [EfChildren] AS [e0] - WHERE [e].[EfParentId] = [e0].[EfParentId]), 0) -FROM [EfParents] AS [e] -``` - - -#### [Count Async](../TestGroup15Aggregation/Test03CountAsync.cs): -- Supported - * Count Children Async (line 44) - * T-Sql executed is - -```SQL -SELECT ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE [e].[EfParentId] = [e0].[EfParentId]) -FROM [EfParents] AS [e] -``` - - * Count Children With Filter Async (line 66) - * T-Sql executed is - -```SQL -SELECT ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE ([e].[EfParentId] = [e0].[EfParentId]) AND ([e0].[ChildInt] = 123)) -FROM [EfParents] AS [e] -``` - - * Count Children With Filter By Closure Async (line 88) - * T-Sql executed is - -```SQL -SELECT ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE ([e].[EfParentId] = [e0].[EfParentId]) AND ([e0].[ChildInt] = [e].[ParentInt])) -FROM [EfParents] AS [e] -``` - - * Count Children With Filter By External Closure Async (line 111) - * T-Sql executed is - -```SQL -SELECT ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE ([e].[EfParentId] = [e0].[EfParentId]) AND ([e0].[ChildInt] = @__i_0)) -FROM [EfParents] AS [e] -``` - - * Count Children With Filter By External Closure2 Async (line 135) - * T-Sql executed is - -```SQL -SELECT ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE ([e].[EfParentId] = [e0].[EfParentId]) AND (([e0].[ChildInt] = @__i_0) AND ([e0].[EfParentId] = @__j_1))) -FROM [EfParents] AS [e] -``` - - * Singleton Count Children With Filter Async (line 157) - * T-Sql executed is - -```SQL -SELECT COUNT(*) -FROM [EfParents] AS [e] -WHERE ( - SELECT COUNT(*) - FROM [EfChildren] AS [e0] - WHERE [e].[EfParentId] = [e0].[EfParentId]) = 2 -``` - - - -### Group: Types -#### [Strings](../TestGroup50Types/Test01Strings.cs): -- Supported - * Concatenate Person Not Handle Null (line 36) - * T-Sql executed is - -```SQL -SELECT ((([e].[FirstName] + N' ') + COALESCE([e].[MiddleName], N'')) + N' ') + [e].[LastName] -FROM [EfPersons] AS [e] -``` - - * Concatenate Person Handle Null (line 57) - * T-Sql executed is - -```SQL -SELECT ((([e].[FirstName] + CASE - WHEN [e].[MiddleName] IS NULL THEN N'' - ELSE N' ' -END) + COALESCE([e].[MiddleName], N'')) + N' ') + [e].[LastName] -FROM [EfPersons] AS [e] -``` - - * Concatenate Person Handle Name Order (line 80) - * T-Sql executed is - -```SQL -SELECT CASE - WHEN [e].[NameOrder] = CAST(1 AS bit) THEN (([e].[LastName] + N', ') + [e].[FirstName]) + CASE - WHEN [e].[MiddleName] IS NULL THEN N'' - ELSE N' ' - END - ELSE ((([e].[FirstName] + CASE - WHEN [e].[MiddleName] IS NULL THEN N'' - ELSE N' ' - END) + COALESCE([e].[MiddleName], N'')) + N' ') + [e].[LastName] -END -FROM [EfPersons] AS [e] -``` - - * Select Generic Method Person Handle (line 101) - * T-Sql executed is - -```SQL -SELECT ((([e].[FirstName] + CASE - WHEN [e].[MiddleName] IS NULL THEN N'' - ELSE N' ' -END) + COALESCE([e].[MiddleName], N'')) + N' ') + [e].[LastName] -FROM [EfPersons] AS [e] -``` - - * Filter Generic Method Person Handle (line 119) - * T-Sql executed is - -```SQL -SELECT [e].[EfPersonId], [e].[FirstName], [e].[LastName], [e].[MiddleName], [e].[NameOrder] -FROM [EfPersons] AS [e] -``` - - -#### [DateTime](../TestGroup50Types/Test05DateTime.cs): -- Supported - * DateTime Where Compare With Static Variable (line 39) - * T-Sql executed is - -```SQL -SELECT [e].[StartDate] -FROM [EfParents] AS [e] -WHERE [e].[StartDate] > '2000-01-01T00:00:00.0000000' -``` - - - -### Group: Additional Features -#### [Nested Expressions](../TestGroup90AdditionalFeatures/Test01NestedExpressions.cs): -- Supported - * Subquery As Context Extension Method (line 72) - * T-Sql executed is - -```SQL -SELECT [e].[EfParentId] AS [ParentId], COALESCE(( - SELECT TOP(1) [e0].[EfChildId] - FROM [EfChildren] AS [e0] - WHERE [e0].[EfParentId] = [e].[EfParentId]), 0) AS [FirstChildId] -FROM [EfParents] AS [e] -``` - - - - -The End diff --git a/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/DocumentationHeaderText.md b/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/DocumentationHeaderText.md deleted file mode 100644 index 3a89a85d..00000000 --- a/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/DocumentationHeaderText.md +++ /dev/null @@ -1,20 +0,0 @@ - -This file documents what linq commands **DelegateDecompiler** supports when -working with [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/) (EF). -EF has one of the best implementations for converting Linq `IQueryable<>` commands into database -access commands, in EF's case T-SQL. Therefore it is a good candidate for using in our tests. - -This documentation was produced by compaired direct EF Linq queries against the same query implemented -as a DelegateDecompiler's `Computed` properties. This produces a Supported/Not Supported flag -on each command type tested. Tests are groups and ordered to try and make finding things -easier. - -So, if you want to use DelegateDecompiler and are not sure whether the linq command -you want to use will work then clone this project and write your own tests. -(See [How to add a test](HowToAddMoreTests.md) documentation on how to do this). -If there is a problem then please fork the repository and add your own tests. -That will make it much easier to diagnose your issue. - -*Note: The test suite has only recently been set up and has only a handful of tests at the moment. -More will appear as we move forward.* - diff --git a/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/SummaryOfSupportedCommands.md b/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/SummaryOfSupportedCommands.md deleted file mode 100644 index 818cac95..00000000 --- a/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/SummaryOfSupportedCommands.md +++ /dev/null @@ -1,62 +0,0 @@ -Summary of supported commands -============ -## Documentation produced for DelegateDecompiler, version 0.34.2.0 on Wednesday, 05 March 2025 13:06 - -This file documents what linq commands **DelegateDecompiler** supports when -working with [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/) (EF). -EF has one of the best implementations for converting Linq `IQueryable<>` commands into database -access commands, in EF's case T-SQL. Therefore it is a good candidate for using in our tests. - -This documentation was produced by compaired direct EF Linq queries against the same query implemented -as a DelegateDecompiler's `Computed` properties. This produces a Supported/Not Supported flag -on each command type tested. Tests are groups and ordered to try and make finding things -easier. - -So, if you want to use DelegateDecompiler and are not sure whether the linq command -you want to use will work then clone this project and write your own tests. -(See [How to add a test](HowToAddMoreTests.md) documentation on how to do this). -If there is a problem then please fork the repository and add your own tests. -That will make it much easier to diagnose your issue. - -*Note: The test suite has only recently been set up and has only a handful of tests at the moment. -More will appear as we move forward.* - - -### Group: Basic Features -- Supported - * [Select](../TestGroup05BasicFeatures/Test01Select.cs) (11 tests) - * [Select Async](../TestGroup05BasicFeatures/Test02SelectAsync.cs) (5 tests) - * [Equals And Not Equals](../TestGroup05BasicFeatures/Test03EqualsAndNotEquals.cs) (4 tests) - * [Nullable](../TestGroup05BasicFeatures/Test04Nullable.cs) (5 tests) - * [Where](../TestGroup05BasicFeatures/Test05Where.cs) (4 tests) - * [Single](../TestGroup05BasicFeatures/Test10Single.cs) (1 tests) - * [Single Async](../TestGroup05BasicFeatures/Test11SingleAsync.cs) (1 tests) - -### Group: Order Take -- Supported - * [Order By](../TestGroup10OrderTake/Test01OrderBy.cs) (3 tests) - * [Skip Take](../TestGroup10OrderTake/Test02SkipTake.cs) (3 tests) - -### Group: Quantifier Operators -- Supported - * [Any](../TestGroup12QuantifierOperators/Test01Any.cs) (2 tests) - * [All](../TestGroup12QuantifierOperators/Test02All.cs) (2 tests) - * [Contains](../TestGroup12QuantifierOperators/Test03Contains.cs) (1 tests) - -### Group: Aggregation -- Supported - * [Count](../TestGroup15Aggregation/Test01Count.cs) (6 tests) - * [Sum](../TestGroup15Aggregation/Test02Sum.cs) (1 tests) - * [Count Async](../TestGroup15Aggregation/Test03CountAsync.cs) (6 tests) - -### Group: Types -- Supported - * [Strings](../TestGroup50Types/Test01Strings.cs) (5 tests) - * [DateTime](../TestGroup50Types/Test05DateTime.cs) (1 tests) - -### Group: Additional Features -- Supported - * [Nested Expressions](../TestGroup90AdditionalFeatures/Test01NestedExpressions.cs) (1 tests) - - -The End diff --git a/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/Test file - please ignore.txt b/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/Test file - please ignore.txt deleted file mode 100644 index 875c32fa..00000000 --- a/src/DelegateDecompiler.EntityFrameworkCore6.Tests/GeneratedDocumentation/Test file - please ignore.txt +++ /dev/null @@ -1 +0,0 @@ -This is a Unit Test of the MarkupFileHelpers \ No newline at end of file diff --git a/test.cmd b/test.cmd index 3af85d9b..70bb5615 100644 --- a/test.cmd +++ b/test.cmd @@ -8,7 +8,6 @@ nunit3-console ^ dotnet test --no-build -c Release -f net8.0 src\DelegateDecompiler.Tests && ^ dotnet test --no-build -c Release -f net8.0 src\DelegateDecompiler.Tests.VB && ^ dotnet test --no-build -c Release -f net8.0 src\DelegateDecompiler.EntityFramework.Tests && ^ -dotnet test --no-build -c Release -f net8.0 src\DelegateDecompiler.EntityFrameworkCore6.Tests && ^ dotnet test --no-build -c Release -f net8.0 src\DelegateDecompiler.EntityFrameworkCore8.Tests && ^ dotnet test --no-build -c Release -f net9.0 src\DelegateDecompiler.EntityFrameworkCore9.Tests && ^ dotnet test --no-build -c Release -f net10.0 src\DelegateDecompiler.EntityFrameworkCore10.Tests From 31c00bd03afd29982b2d68003715aba0397d857f Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Wed, 19 Nov 2025 19:53:23 +1000 Subject: [PATCH 09/13] Update EntityFramework to 6.5.1 (#306) +semver:feature --- .../DelegateDecompiler.EntityFramework.csproj | 2 +- .../DelegateDecompiler.EntityFrameworkCore10.Tests.csproj | 1 - .../DelegateDecompiler.EntityFrameworkCore8.Tests.csproj | 3 +-- .../DelegateDecompiler.EntityFrameworkCore9.Tests.csproj | 1 - src/Directory.Build.props | 2 +- 5 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/DelegateDecompiler.EntityFramework/DelegateDecompiler.EntityFramework.csproj b/src/DelegateDecompiler.EntityFramework/DelegateDecompiler.EntityFramework.csproj index 764cd29b..26d71d8b 100644 --- a/src/DelegateDecompiler.EntityFramework/DelegateDecompiler.EntityFramework.csproj +++ b/src/DelegateDecompiler.EntityFramework/DelegateDecompiler.EntityFramework.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/DelegateDecompiler.EntityFrameworkCore10.Tests/DelegateDecompiler.EntityFrameworkCore10.Tests.csproj b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/DelegateDecompiler.EntityFrameworkCore10.Tests.csproj index 7ee69f0b..e1485bf7 100644 --- a/src/DelegateDecompiler.EntityFrameworkCore10.Tests/DelegateDecompiler.EntityFrameworkCore10.Tests.csproj +++ b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/DelegateDecompiler.EntityFrameworkCore10.Tests.csproj @@ -24,7 +24,6 @@ - diff --git a/src/DelegateDecompiler.EntityFrameworkCore8.Tests/DelegateDecompiler.EntityFrameworkCore8.Tests.csproj b/src/DelegateDecompiler.EntityFrameworkCore8.Tests/DelegateDecompiler.EntityFrameworkCore8.Tests.csproj index 7a96b897..eebda5f4 100644 --- a/src/DelegateDecompiler.EntityFrameworkCore8.Tests/DelegateDecompiler.EntityFrameworkCore8.Tests.csproj +++ b/src/DelegateDecompiler.EntityFrameworkCore8.Tests/DelegateDecompiler.EntityFrameworkCore8.Tests.csproj @@ -24,8 +24,7 @@ - - + diff --git a/src/DelegateDecompiler.EntityFrameworkCore9.Tests/DelegateDecompiler.EntityFrameworkCore9.Tests.csproj b/src/DelegateDecompiler.EntityFrameworkCore9.Tests/DelegateDecompiler.EntityFrameworkCore9.Tests.csproj index 036f8f3e..36e656c4 100644 --- a/src/DelegateDecompiler.EntityFrameworkCore9.Tests/DelegateDecompiler.EntityFrameworkCore9.Tests.csproj +++ b/src/DelegateDecompiler.EntityFrameworkCore9.Tests/DelegateDecompiler.EntityFrameworkCore9.Tests.csproj @@ -24,7 +24,6 @@ - diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 075f5826..80058c8b 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -27,7 +27,7 @@ true low - direct + all $(CI) From 07ebad8dd4a72fae7699c59b71883e724de9229b Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Wed, 19 Nov 2025 20:03:47 +1000 Subject: [PATCH 10/13] Update Microsoft.NET.Test.Sdk to 17.3.3 (#307) +semver:patch --- .../DelegateDecompiler.EntityFramework.Tests.csproj | 2 +- .../DelegateDecompiler.EntityFrameworkCore10.Tests.csproj | 2 +- .../DelegateDecompiler.EntityFrameworkCore8.Tests.csproj | 2 +- .../DelegateDecompiler.EntityFrameworkCore9.Tests.csproj | 2 +- .../DelegateDecompiler.Tests.VB.vbproj | 2 +- src/DelegateDecompiler.Tests/DelegateDecompiler.Tests.csproj | 3 +-- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/DelegateDecompiler.EntityFramework.Tests/DelegateDecompiler.EntityFramework.Tests.csproj b/src/DelegateDecompiler.EntityFramework.Tests/DelegateDecompiler.EntityFramework.Tests.csproj index 59b7c3bf..07f52af1 100644 --- a/src/DelegateDecompiler.EntityFramework.Tests/DelegateDecompiler.EntityFramework.Tests.csproj +++ b/src/DelegateDecompiler.EntityFramework.Tests/DelegateDecompiler.EntityFramework.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/DelegateDecompiler.EntityFrameworkCore10.Tests/DelegateDecompiler.EntityFrameworkCore10.Tests.csproj b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/DelegateDecompiler.EntityFrameworkCore10.Tests.csproj index e1485bf7..9a091a86 100644 --- a/src/DelegateDecompiler.EntityFrameworkCore10.Tests/DelegateDecompiler.EntityFrameworkCore10.Tests.csproj +++ b/src/DelegateDecompiler.EntityFrameworkCore10.Tests/DelegateDecompiler.EntityFrameworkCore10.Tests.csproj @@ -25,7 +25,7 @@ - + diff --git a/src/DelegateDecompiler.EntityFrameworkCore8.Tests/DelegateDecompiler.EntityFrameworkCore8.Tests.csproj b/src/DelegateDecompiler.EntityFrameworkCore8.Tests/DelegateDecompiler.EntityFrameworkCore8.Tests.csproj index eebda5f4..67bb42a7 100644 --- a/src/DelegateDecompiler.EntityFrameworkCore8.Tests/DelegateDecompiler.EntityFrameworkCore8.Tests.csproj +++ b/src/DelegateDecompiler.EntityFrameworkCore8.Tests/DelegateDecompiler.EntityFrameworkCore8.Tests.csproj @@ -25,7 +25,7 @@ - + diff --git a/src/DelegateDecompiler.EntityFrameworkCore9.Tests/DelegateDecompiler.EntityFrameworkCore9.Tests.csproj b/src/DelegateDecompiler.EntityFrameworkCore9.Tests/DelegateDecompiler.EntityFrameworkCore9.Tests.csproj index 36e656c4..9771e027 100644 --- a/src/DelegateDecompiler.EntityFrameworkCore9.Tests/DelegateDecompiler.EntityFrameworkCore9.Tests.csproj +++ b/src/DelegateDecompiler.EntityFrameworkCore9.Tests/DelegateDecompiler.EntityFrameworkCore9.Tests.csproj @@ -25,7 +25,7 @@ - + diff --git a/src/DelegateDecompiler.Tests.VB/DelegateDecompiler.Tests.VB.vbproj b/src/DelegateDecompiler.Tests.VB/DelegateDecompiler.Tests.VB.vbproj index 20dd1c20..2e6a743a 100644 --- a/src/DelegateDecompiler.Tests.VB/DelegateDecompiler.Tests.VB.vbproj +++ b/src/DelegateDecompiler.Tests.VB/DelegateDecompiler.Tests.VB.vbproj @@ -5,7 +5,7 @@ - + diff --git a/src/DelegateDecompiler.Tests/DelegateDecompiler.Tests.csproj b/src/DelegateDecompiler.Tests/DelegateDecompiler.Tests.csproj index 39571f69..7cddc036 100644 --- a/src/DelegateDecompiler.Tests/DelegateDecompiler.Tests.csproj +++ b/src/DelegateDecompiler.Tests/DelegateDecompiler.Tests.csproj @@ -4,8 +4,7 @@ - - + From 4139be760cb085ee8b1dc2b6b9e6b7073c8e5e46 Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Wed, 19 Nov 2025 22:02:00 +1000 Subject: [PATCH 11/13] Add support for ldobj opcode (#309) +semver:fix --- .github/workflows/.NET-Framework.yml | 1 - .github/workflows/dotnet-linux.yml | 1 - .github/workflows/dotnet-windows.yml | 1 - src/DelegateDecompiler.Tests/Issue308.cs | 59 ++++++++++++++++++++++++ src/DelegateDecompiler/Processor.cs | 1 + 5 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 src/DelegateDecompiler.Tests/Issue308.cs diff --git a/.github/workflows/.NET-Framework.yml b/.github/workflows/.NET-Framework.yml index ff4df870..2cc58836 100644 --- a/.github/workflows/.NET-Framework.yml +++ b/.github/workflows/.NET-Framework.yml @@ -4,7 +4,6 @@ on: push: branches: [ main ] pull_request: - branches: [ main ] jobs: dotnet-framework: diff --git a/.github/workflows/dotnet-linux.yml b/.github/workflows/dotnet-linux.yml index cfa433d3..c62b64b3 100644 --- a/.github/workflows/dotnet-linux.yml +++ b/.github/workflows/dotnet-linux.yml @@ -4,7 +4,6 @@ on: push: branches: [ main ] pull_request: - branches: [ main ] workflow_dispatch: jobs: diff --git a/.github/workflows/dotnet-windows.yml b/.github/workflows/dotnet-windows.yml index f9988079..d6dda315 100644 --- a/.github/workflows/dotnet-windows.yml +++ b/.github/workflows/dotnet-windows.yml @@ -4,7 +4,6 @@ on: push: branches: [ main ] pull_request: - branches: [ main ] workflow_dispatch: jobs: diff --git a/src/DelegateDecompiler.Tests/Issue308.cs b/src/DelegateDecompiler.Tests/Issue308.cs new file mode 100644 index 00000000..0eb6956c --- /dev/null +++ b/src/DelegateDecompiler.Tests/Issue308.cs @@ -0,0 +1,59 @@ +using System; +using NUnit.Framework; + +namespace DelegateDecompiler.Tests; + +[TestFixture] +public class Issue308 : DecompilerTestsBase +{ + [Test] + public void TestIsValidAt1() + { + static bool Actual(TEntity entity, DateTime dateTime) where TEntity : ITimeValidity => + entity.IsValidAt(dateTime); + + Test(Actual, (entity, dateTime) => entity.IsValidAt(dateTime)); + } + + [Test] + public void TestIsValidAt2() + { + static bool Actual(TEntity entity, IDateTimeProvider dateTimeProvider) + where TEntity : ITimeValidity => entity.IsValidAt(dateTimeProvider.Now); + + Test(Actual, (entity, dateTimeProvider) => entity.IsValidAt(dateTimeProvider.Now)); + } + + [Test] + public void TestIsValidAt3() + { + static bool Actual(TEntity entity, DateTime dateTime) where TEntity : ITimeValidity => + entity.IsValidAt(DateTime.Now); + + Test(Actual, (entity, dateTime) => entity.IsValidAt(DateTime.Now)); + } + + [Test] + public void TestIsValidAt4() + { + static bool Actual(TEntity entity) where TEntity : ITimeValidity => + entity.IsValidAt(DateTime.Now); + + Test(Actual, entity => entity.IsValidAt(DateTime.Now)); + } + + interface IDateTimeProvider + { + public DateTime Now { get; } + } + + interface ITimeValidity + { + public bool IsValidAt(DateTime time); + } + + class Entity : ITimeValidity + { + public bool IsValidAt(DateTime time) => default; + } +} diff --git a/src/DelegateDecompiler/Processor.cs b/src/DelegateDecompiler/Processor.cs index aa5271ca..8efc69e0 100644 --- a/src/DelegateDecompiler/Processor.cs +++ b/src/DelegateDecompiler/Processor.cs @@ -148,6 +148,7 @@ static int ProcessInstruction(ProcessorState state, Instruction instruction) if (instruction.OpCode == OpCodes.Nop || instruction.OpCode == OpCodes.Break || instruction.OpCode == OpCodes.Ret || + instruction.OpCode == OpCodes.Ldobj || instruction.OpCode.OpCodeType == OpCodeType.Prefix || instruction.OpCode.FlowControl == FlowControl.Branch) { From f56be94ff3518730c8b122365001f0e8c874bb50 Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Wed, 19 Nov 2025 22:20:24 +1000 Subject: [PATCH 12/13] Fix creating release from other branches (#311) +semver:patch --- .github/workflows/create-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 67d7ab97..e0d4159d 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -22,6 +22,6 @@ jobs: uses: gittools/actions/gitversion/execute@v4 - name: Create Release - run: gh release create ${{ steps.gitversion.outputs.majorMinorPatch }} --generate-notes + run: gh release create ${{ steps.gitversion.outputs.majorMinorPatch }} --generate-notes --target $GITHUB_SHA env: GH_TOKEN: ${{ secrets.GH_TOKEN }} \ No newline at end of file From 2d93e7c20efa55bed13e2aa192e40980e822e90e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Nov 2025 19:06:31 +0000 Subject: [PATCH 13/13] Initial plan