From b083221a883aed47e0841b4032750f4ca389beb4 Mon Sep 17 00:00:00 2001 From: Koen <2912652+koenbeuk@users.noreply.github.com> Date: Sun, 13 Sep 2026 00:14:45 +0000 Subject: [PATCH 1/6] Add .NET 11 (RC 1) support - net11.0 TFM across all packages; EF Core 11.0.0-rc.1.26425.128 via per-TFM VersionOverride (src, integration tests, sample) - NoWarn NU5104 on the two EF packages so stable packs accept the go-live RC dependency; remove at EF 11 GA - Rework exact-TFM conditions that broke on net11.0: Pomelo gating (PomeloMySqlSupported), new NpgsqlSupported gate (no EF 11-compatible Npgsql release yet), extension-member test exclusion via IsTargetFrameworkCompatible - Generator tests: Basic.Reference.Assemblies.Net110 + NET11_0_OR_GREATER branch in GeneratorTestBase; snapshots unchanged - CI/release: separate setup-dotnet step for the 11.0.x RC SDK (dotnet-quality: preview), Cosmos TFM loop extended; devcontainer installs SDK 11 - EF Core 11 limitation: window functions other than RowNumber throw NotSupportedException on net11.0 - EF 11 removed the QuerySqlGenerator VisitChildren fallback that WindowFunctionSqlExpression's self-rendering relied on (dotnet/efcore#37533, tracked in dotnet/efcore#38977). RowNumber and indexed Select keep working; affected tests gated, limitation documented Co-Authored-By: Claude Fable 5 --- .devcontainer/devcontainer.json | 3 +- .github/workflows/ci.yml | 29 ++++++++++++++++++- .github/workflows/release.yml | 9 ++++++ CLAUDE.md | 25 +++++++++------- Directory.Build.props | 2 +- Directory.Packages.props | 1 + docs/advanced/testing-strategy.md | 2 +- docs/guide/quickstart.md | 2 +- docs/guide/window-functions.md | 4 +++ samples/EFCoreSample/EFCoreSample.csproj | 3 ++ ...yFrameworkCore.RelationalExtensions.csproj | 5 ++++ .../WindowFunctionMethodCallTranslator.cs | 14 +++++++++ ...ExpressiveSharp.EntityFrameworkCore.csproj | 5 ++++ ...ntityFrameworkCore.IntegrationTests.csproj | 24 +++++++++++---- .../Infrastructure/WindowFunctionTestBase.cs | 23 +++++++++++++++ .../ExpressiveSharp.Generator.Tests.csproj | 1 + .../Infrastructure/GeneratorTestBase.cs | 4 ++- .../ExpressiveSharp.IntegrationTests.csproj | 4 +-- 18 files changed, 137 insertions(+), 23 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b22b0193..1de57440 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,8 @@ "ghcr.io/devcontainers/features/dotnet:2": { "additionalVersions": [ "8.0", - "9.0" + "9.0", + "11.0" ] }, "ghcr.io/devcontainers/features/node:1": {}, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1913a7e..2ca234f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,15 @@ jobs: 9.0.x 10.0.x + # Separate step: dotnet-quality applies to every wildcard version in a + # step, and the 8/9/10 SDKs above must keep resolving to GA releases. + # Fold into the block above (and drop the quality flag) at .NET 11 GA. + - name: Setup .NET 11 SDK (RC) + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 11.0.x + dotnet-quality: preview + - name: Cache NuGet packages uses: actions/cache@v4 with: @@ -135,6 +144,15 @@ jobs: 9.0.x 10.0.x + # Separate step: dotnet-quality applies to every wildcard version in a + # step, and the 8/9/10 SDKs above must keep resolving to GA releases. + # Fold into the block above (and drop the quality flag) at .NET 11 GA. + - name: Setup .NET 11 SDK (RC) + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 11.0.x + dotnet-quality: preview + - name: Cache NuGet packages uses: actions/cache@v4 with: @@ -163,7 +181,7 @@ jobs: # connects to the gateway-advertised endpoint, which must # match the host-side port), so only one emulator can run # at a time. Run TFMs sequentially. - for tfm in net8.0 net9.0 net10.0; do + for tfm in net8.0 net9.0 net10.0 net11.0; do echo "::group::Cosmos tests ($tfm)" dotnet test --no-build -c Release \ --project tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests \ @@ -220,6 +238,15 @@ jobs: 9.0.x 10.0.x + # Separate step: dotnet-quality applies to every wildcard version in a + # step, and the 8/9/10 SDKs above must keep resolving to GA releases. + # Fold into the block above (and drop the quality flag) at .NET 11 GA. + - name: Setup .NET 11 SDK (RC) + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 11.0.x + dotnet-quality: preview + - name: Cache NuGet packages uses: actions/cache@v4 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 520d02f0..ac806b98 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,6 +35,15 @@ jobs: 9.0.x 10.0.x + # Separate step: dotnet-quality applies to every wildcard version in a + # step, and the 8/9/10 SDKs above must keep resolving to GA releases. + # Fold into the block above (and drop the quality flag) at .NET 11 GA. + - name: Setup .NET 11 SDK (RC) + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 11.0.x + dotnet-quality: preview + - name: Cache NuGet packages uses: actions/cache@v4 with: diff --git a/CLAUDE.md b/CLAUDE.md index 1919a7e7..e7fc25c4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,7 +35,7 @@ dotnet run -c Release --project benchmarks/ExpressiveSharp.Benchmarks/Expressive dotnet run -c Release --project benchmarks/ExpressiveSharp.Benchmarks/ExpressiveSharp.Benchmarks.csproj -- --filter "*GeneratorBenchmarks*" ``` -CI targets both .NET 8.0 and .NET 10.0 SDKs. +CI targets the .NET 8.0, 9.0, 10.0, and 11.0 (RC) SDKs. ## Architecture @@ -67,12 +67,12 @@ CI targets both .NET 8.0 and .NET 10.0 SDKs. ### Project Dependencies ``` -ExpressiveSharp.Abstractions (attributes + source generator, net8.0;net10.0) +ExpressiveSharp.Abstractions (attributes + source generator, net8.0;net9.0;net10.0;net11.0) └── no external deps Provides: [Expressive], [ExpressiveFor], [ExpressiveForConstructor], [PolyfillTarget], IExpressionTreeTransformer, source generator + code fixers (as analyzers) -ExpressiveSharp (core runtime, net8.0;net10.0) +ExpressiveSharp (core runtime, net8.0;net9.0;net10.0;net11.0) └── ExpressiveSharp.Abstractions Provides: ExpressiveResolver, ExpressiveReplacer, expression transformers, IExpressiveQueryable, ExpressionPolyfill, .ExpandExpressives(), .AsExpressive() @@ -80,20 +80,20 @@ ExpressiveSharp (core runtime, net8.0;net10.0) ExpressiveSharp.Generator (source generator, netstandard2.0) └── Microsoft.CodeAnalysis.CSharp 5.0.0 -ExpressiveSharp.MongoDB (net8.0;net10.0) +ExpressiveSharp.MongoDB (net8.0;net9.0;net10.0;net11.0) ├── ExpressiveSharp ├── MongoDB.Driver 3.4.0 └── Provides: IRewritableMongoQueryable, ExpressiveMongoCollection, ExpressiveMongoQueryProvider (decorating IQueryProvider/IMongoQueryProvider), async lambda stubs with [PolyfillTarget(typeof(MongoQueryable))] -ExpressiveSharp.EntityFrameworkCore (net8.0;net10.0) +ExpressiveSharp.EntityFrameworkCore (net8.0;net9.0;net10.0;net11.0) ├── ExpressiveSharp - ├── EF Core 8.0.25 / 10.0.0 + ├── EF Core 8.0.25 / 9.0.0 / 10.0.0 / 11.0.0-rc.1 (per-TFM VersionOverride) └── Provides: ExpressiveDbSet, IIncludableExpressiveQueryable, chain-continuity stubs, async lambda stubs with [PolyfillTarget] -ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions (net8.0;net10.0) +ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions (net8.0;net9.0;net10.0;net11.0) └── no external deps Provides: Pure marker types for window functions — no EF Core dependency: WindowFunction (ranking: ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST; @@ -101,16 +101,21 @@ ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions (net8.0;ne LAST_VALUE, NTH_VALUE), Window, OrderedWindowDefinition, PartitionedWindowDefinition, FramedWindowDefinition, WindowFrameBound -ExpressiveSharp.EntityFrameworkCore.RelationalExtensions (net8.0;net10.0) +ExpressiveSharp.EntityFrameworkCore.RelationalExtensions (net8.0;net9.0;net10.0;net11.0) ├── ExpressiveSharp.EntityFrameworkCore ├── ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions - ├── EF Core Relational 8.0.25 / 10.0.0 + ├── EF Core Relational 8.0.25 / 9.0.0 / 10.0.0 / 11.0.0-rc.1 (per-TFM VersionOverride) └── Provides: Window function SQL translation (ranking, aggregate with ROWS/RANGE frame clauses, navigation with LAG/LEAD/FIRST_VALUE/LAST_VALUE/NTH_VALUE), indexed Select, activated via UseExpressives(o => o.UseRelationalExtensions()). Translators: WindowFunctionMethodCallTranslator, WindowSpecMethodCallTranslator, WindowFrameBoundMemberTranslator (IMemberTranslatorPlugin for property getters). Note: NTH_VALUE is not supported on SQL Server. + EF Core 11 limitation: all window functions except RowNumber throw + NotSupportedException on net11.0 — EF 11 removed the QuerySqlGenerator + VisitChildren fallback that WindowFunctionSqlExpression's self-rendering + relied on (dotnet/efcore#37533). RowNumber and indexed Select still work + (built-in RowNumberExpression). Revisit if EF 11 GA restores a hook. ExpressiveSharp.EntityFrameworkCore.CodeFixers (Roslyn analyzer, netstandard2.0) └── Microsoft.CodeAnalysis.CSharp.Workspaces 4.12.0 @@ -145,7 +150,7 @@ Snapshot tests use `GeneratorTestBase.RunExpressiveGenerator()` to compile via R - `TreatWarningsAsErrors: true` — all warnings are errors - `Nullable: enable` — full nullable reference types -- C# 12.0 on net8.0, C# 14.0 on net10.0 +- C# 14.0 on all TFMs (net8.0–net11.0; `LangVersion` is set globally in `Directory.Build.props`) - Allman brace style, 4-space indentation, `var` preferred - Instance fields: `_camelCase` - Expression-bodied members preferred for methods/properties diff --git a/Directory.Build.props b/Directory.Build.props index dbae29cc..3f10bb04 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - net8.0;net9.0;net10.0 + net8.0;net9.0;net10.0;net11.0 true 14.0 enable diff --git a/Directory.Packages.props b/Directory.Packages.props index 061e9371..28e8cae9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -24,6 +24,7 @@ + diff --git a/docs/advanced/testing-strategy.md b/docs/advanced/testing-strategy.md index cc0af5f5..eb4d1066 100644 --- a/docs/advanced/testing-strategy.md +++ b/docs/advanced/testing-strategy.md @@ -103,7 +103,7 @@ dotnet test --filter "FullyQualifiedName~MyTestName" dotnet test -f net8.0 -c Release ``` -CI targets both .NET 8.0 and .NET 10.0 SDKs. +CI targets the .NET 8.0, 9.0, 10.0, and 11.0 SDKs. ## Performance Benchmarks diff --git a/docs/guide/quickstart.md b/docs/guide/quickstart.md index 0b9947ae..c93ca0f6 100644 --- a/docs/guide/quickstart.md +++ b/docs/guide/quickstart.md @@ -4,7 +4,7 @@ This guide walks you through a complete end-to-end example — from installing t ## Prerequisites -- .NET 8 SDK or later (.NET 10 also supported) +- .NET 8 SDK or later (.NET 9, 10, and 11 also supported) - A LINQ provider. ExpressiveSharp integrates with **EF Core**, **MongoDB**, or **any `IQueryable`**. ## Step 1 — Install the Packages diff --git a/docs/guide/window-functions.md b/docs/guide/window-functions.md index 392e318b..8212fdf6 100644 --- a/docs/guide/window-functions.md +++ b/docs/guide/window-functions.md @@ -2,6 +2,10 @@ ExpressiveSharp provides SQL window function support through the `ExpressiveSharp.EntityFrameworkCore.RelationalExtensions` package. This enables ranking (ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST), aggregate (SUM, AVG, COUNT, MIN, MAX), and navigation (LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE) functions directly in LINQ queries with a fluent window specification API. +::: warning Not supported on EF Core 11 +EF Core 11 removed the SQL-generation fallback for third-party SQL expressions ([dotnet/efcore#37533](https://github.com/dotnet/efcore/pull/37533)), which the window function translation relies on. On the `net11.0` target, every window function except `WindowFunction.RowNumber` throws `NotSupportedException` at query translation. `WindowFunction.RowNumber` and indexed `Select` are unaffected — they map to EF Core's built-in `ROW_NUMBER()` support. All functions remain fully supported on EF Core 8, 9, and 10. +::: + ## Installation ```bash diff --git a/samples/EFCoreSample/EFCoreSample.csproj b/samples/EFCoreSample/EFCoreSample.csproj index 7181d13d..e9061488 100644 --- a/samples/EFCoreSample/EFCoreSample.csproj +++ b/samples/EFCoreSample/EFCoreSample.csproj @@ -14,6 +14,9 @@ + diff --git a/src/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.csproj b/src/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.csproj index d07e4595..a59f1e22 100644 --- a/src/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.csproj +++ b/src/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.csproj @@ -2,6 +2,8 @@ Relational database extensions for ExpressiveSharp EF Core — SQL window functions (ranking, aggregate, navigation) with PARTITION BY / ORDER BY / ROWS|RANGE frame support, plus indexed Select. + + $(NoWarn);NU5104 + $(NoWarn);NU5104 $(NoWarn);NU1903;NU1902;NU1901;NU1904 + + + true + true $(DefineConstants);TEST_SQLSERVER - + $(DefineConstants);TEST_POSTGRES $(DefineConstants);TEST_COSMOS - + $(DefineConstants);TEST_POMELO_MYSQL @@ -36,6 +41,9 @@ + @@ -49,10 +57,13 @@ + - + @@ -77,10 +88,13 @@ + - - + + diff --git a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/WindowFunctionTestBase.cs b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/WindowFunctionTestBase.cs index 309569e7..78b55adf 100644 --- a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/WindowFunctionTestBase.cs +++ b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/WindowFunctionTestBase.cs @@ -57,6 +57,9 @@ public async Task RowNumber_ReturnsCorrectSequentialNumbers() } } + // All window functions except RowNumber are unsupported on EF Core 11 — the translator + // throws NotSupportedException (see WindowFunctionMethodCallTranslator). +#if !NET11_0_OR_GREATER [TestMethod] public async Task Rank_WithTies_ReturnsGaps() { @@ -135,6 +138,7 @@ public async Task Ntile_DistributesIntoBuckets() Assert.AreEqual(2, bucketCounts[2]); Assert.AreEqual(2, bucketCounts[3]); } +#endif [TestMethod] public async Task RowNumber_WithPartitionBy_ResetsPerGroup() @@ -179,6 +183,7 @@ public async Task IndexedSelect_ReturnsZeroBasedIndices() CollectionAssert.AreEqual(new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, positions); } +#if !NET11_0_OR_GREATER [TestMethod] public async Task MultipleWindowFunctions_InSameSelect() { @@ -203,6 +208,7 @@ public async Task MultipleWindowFunctions_InSameSelect() Assert.AreEqual(9, results[9].Dense); Assert.AreEqual(4, results[9].Quartile); } +#endif // RowNumber over o.Total (which is [Expressive] => Price * Quantity). The // ExpressiveQueryCompiler must expand Total inside the OrderBy argument of @@ -246,6 +252,7 @@ public async Task RowNumber_OverExpressiveTotal_UsesExpandedExpression() Assert.IsTrue(results[i].Total >= results[i - 1].Total); } +#if !NET11_0_OR_GREATER // Aggregate window functions (SUM, AVG, COUNT, MIN, MAX) depend on the frame clause. // Seed prices (ASC): 10, 15, 20, 20, 25, 30, 35, 40, 45, 50 // Running total: 10, 25, 45, 65, 90, 120, 155, 195, 240, 290 @@ -963,6 +970,7 @@ public async Task LastValue_DefaultFrame_ReturnsCurrentRow() Assert.AreEqual(r.Price, r.Last, $"With the default frame, LAST_VALUE should return the current row's price (got {r.Last} vs {r.Price})"); } +#endif [TestMethod] public void Ntile_ZeroBuckets_ThrowsAtTranslation() @@ -1032,4 +1040,19 @@ public void NthValue_ZeroPosition_ThrowsAtTranslation() StringAssert.Contains(ex.Message, "NthValue"); StringAssert.Contains(ex.Message, "1-based"); } + +#if NET11_0_OR_GREATER + [TestMethod] + public void WindowFunction_ThrowsNotSupported_OnEfCore11() + { + var query = Context.Orders.Select(o => new + { + o.Id, + PriceRank = WindowFunction.Rank(Window.OrderBy(o.Price)), + }); + + var ex = Assert.ThrowsExactly(() => query.ToQueryString()); + StringAssert.Contains(ex.Message, "EF Core 11"); + } +#endif } diff --git a/tests/ExpressiveSharp.Generator.Tests/ExpressiveSharp.Generator.Tests.csproj b/tests/ExpressiveSharp.Generator.Tests/ExpressiveSharp.Generator.Tests.csproj index b02c46cc..ee704574 100644 --- a/tests/ExpressiveSharp.Generator.Tests/ExpressiveSharp.Generator.Tests.csproj +++ b/tests/ExpressiveSharp.Generator.Tests/ExpressiveSharp.Generator.Tests.csproj @@ -12,6 +12,7 @@ + diff --git a/tests/ExpressiveSharp.Generator.Tests/Infrastructure/GeneratorTestBase.cs b/tests/ExpressiveSharp.Generator.Tests/Infrastructure/GeneratorTestBase.cs index af8f79ad..04792c4f 100644 --- a/tests/ExpressiveSharp.Generator.Tests/Infrastructure/GeneratorTestBase.cs +++ b/tests/ExpressiveSharp.Generator.Tests/Infrastructure/GeneratorTestBase.cs @@ -44,7 +44,9 @@ protected sealed class TestGeneratorRunResult protected IReadOnlyList GetDefaultReferences() { var references = Basic.Reference.Assemblies. -#if NET10_0_OR_GREATER +#if NET11_0_OR_GREATER + Net110 +#elif NET10_0_OR_GREATER Net100 #elif NET9_0 Net90 diff --git a/tests/ExpressiveSharp.IntegrationTests/ExpressiveSharp.IntegrationTests.csproj b/tests/ExpressiveSharp.IntegrationTests/ExpressiveSharp.IntegrationTests.csproj index 78b68eba..25634486 100644 --- a/tests/ExpressiveSharp.IntegrationTests/ExpressiveSharp.IntegrationTests.csproj +++ b/tests/ExpressiveSharp.IntegrationTests/ExpressiveSharp.IntegrationTests.csproj @@ -11,8 +11,8 @@ OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> - - + + From 2faa969ced32fb7147afe31b684d476bce420268 Mon Sep 17 00:00:00 2001 From: Koen <2912652+koenbeuk@users.noreply.github.com> Date: Sun, 20 Sep 2026 02:06:38 +0000 Subject: [PATCH 2/6] Drop net11.0 from RelationalExtensions until EF Core 11 restores an extension point MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EF Core 11 is now unsupported for the RelationalExtensions packages instead of shipping a net11.0 asset whose window functions throw: - RelationalExtensions + RelationalExtensions.Abstractions no longer target net11.0 (dotnet/efcore#37533 removed the QuerySqlGenerator VisitChildren fallback; no replacement hook — tracked in dotnet/efcore#38977) - Revert the net11-only NotSupportedException guard in WindowFunctionMethodCallTranslator (dead code without the TFM) and the test gates in WindowFunctionTestBase - Test projects gate on a RelationalExtensionsSupported property: project references and window-function/RelationalExtensions test files are excluded on net11.0; test factories fall back to plain UseExpressives() - RelationalExtensions no longer needs the NU5104 suppression (no prerelease dependency left) - Docs: window-functions guide and CLAUDE.md now state EF Core 11 is unsupported and recommend EF Core 10 on .NET 11 for window functions Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 17 +++++++------- docs/guide/window-functions.md | 2 +- ...e.RelationalExtensions.Abstractions.csproj | 2 ++ ...yFrameworkCore.RelationalExtensions.csproj | 10 ++++---- .../WindowFunctionMethodCallTranslator.cs | 14 ----------- ...ntityFrameworkCore.IntegrationTests.csproj | 15 ++++++++++-- .../Infrastructure/TestContextFactories.cs | 9 ++++++++ .../Infrastructure/WindowFunctionTestBase.cs | 23 ------------------- .../ExpressiveSharp.Tests.csproj | 14 +++++++++-- 9 files changed, 51 insertions(+), 55 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index e7fc25c4..d2f87010 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -93,7 +93,7 @@ ExpressiveSharp.EntityFrameworkCore (net8.0;net9.0;net10.0;net11.0) └── Provides: ExpressiveDbSet, IIncludableExpressiveQueryable, chain-continuity stubs, async lambda stubs with [PolyfillTarget] -ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions (net8.0;net9.0;net10.0;net11.0) +ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions (net8.0;net9.0;net10.0 — no net11.0, see below) └── no external deps Provides: Pure marker types for window functions — no EF Core dependency: WindowFunction (ranking: ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST; @@ -101,21 +101,22 @@ ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions (net8.0;ne LAST_VALUE, NTH_VALUE), Window, OrderedWindowDefinition, PartitionedWindowDefinition, FramedWindowDefinition, WindowFrameBound -ExpressiveSharp.EntityFrameworkCore.RelationalExtensions (net8.0;net9.0;net10.0;net11.0) +ExpressiveSharp.EntityFrameworkCore.RelationalExtensions (net8.0;net9.0;net10.0 — no net11.0, see below) ├── ExpressiveSharp.EntityFrameworkCore ├── ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions - ├── EF Core Relational 8.0.25 / 9.0.0 / 10.0.0 / 11.0.0-rc.1 (per-TFM VersionOverride) + ├── EF Core Relational 8.0.25 / 9.0.0 / 10.0.0 (per-TFM VersionOverride) └── Provides: Window function SQL translation (ranking, aggregate with ROWS/RANGE frame clauses, navigation with LAG/LEAD/FIRST_VALUE/LAST_VALUE/NTH_VALUE), indexed Select, activated via UseExpressives(o => o.UseRelationalExtensions()). Translators: WindowFunctionMethodCallTranslator, WindowSpecMethodCallTranslator, WindowFrameBoundMemberTranslator (IMemberTranslatorPlugin for property getters). Note: NTH_VALUE is not supported on SQL Server. - EF Core 11 limitation: all window functions except RowNumber throw - NotSupportedException on net11.0 — EF 11 removed the QuerySqlGenerator - VisitChildren fallback that WindowFunctionSqlExpression's self-rendering - relied on (dotnet/efcore#37533). RowNumber and indexed Select still work - (built-in RowNumberExpression). Revisit if EF 11 GA restores a hook. + EF Core 11 is unsupported: both RelationalExtensions packages deliberately do + not target net11.0 — EF 11 removed the QuerySqlGenerator VisitChildren fallback + that WindowFunctionSqlExpression's self-rendering relied on, with no replacement + extension point (dotnet/efcore#37533; tracked in dotnet/efcore#38977). Test + projects gate on a RelationalExtensionsSupported msbuild property. Add the TFM + back (and drop those gates) once EF provides a hook. ExpressiveSharp.EntityFrameworkCore.CodeFixers (Roslyn analyzer, netstandard2.0) └── Microsoft.CodeAnalysis.CSharp.Workspaces 4.12.0 diff --git a/docs/guide/window-functions.md b/docs/guide/window-functions.md index 8212fdf6..96d18d45 100644 --- a/docs/guide/window-functions.md +++ b/docs/guide/window-functions.md @@ -3,7 +3,7 @@ ExpressiveSharp provides SQL window function support through the `ExpressiveSharp.EntityFrameworkCore.RelationalExtensions` package. This enables ranking (ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST), aggregate (SUM, AVG, COUNT, MIN, MAX), and navigation (LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE) functions directly in LINQ queries with a fluent window specification API. ::: warning Not supported on EF Core 11 -EF Core 11 removed the SQL-generation fallback for third-party SQL expressions ([dotnet/efcore#37533](https://github.com/dotnet/efcore/pull/37533)), which the window function translation relies on. On the `net11.0` target, every window function except `WindowFunction.RowNumber` throws `NotSupportedException` at query translation. `WindowFunction.RowNumber` and indexed `Select` are unaffected — they map to EF Core's built-in `ROW_NUMBER()` support. All functions remain fully supported on EF Core 8, 9, and 10. +EF Core 11 removed the SQL-generation fallback for third-party SQL expressions ([dotnet/efcore#37533](https://github.com/dotnet/efcore/pull/37533)), which the window function translation relies on. Until EF Core provides a replacement extension point (tracked in [dotnet/efcore#38977](https://github.com/dotnet/efcore/issues/38977)), the `RelationalExtensions` packages do not ship a `net11.0` target and EF Core 11 is unsupported. Applications on .NET 11 that need window functions should stay on EF Core 10, which runs fine on the .NET 11 runtime. All functions remain fully supported on EF Core 8, 9, and 10. ::: ## Installation diff --git a/src/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions.csproj b/src/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions.csproj index be7a19a8..c9b562cd 100644 --- a/src/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions.csproj +++ b/src/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.Abstractions.csproj @@ -2,6 +2,8 @@ Pure marker types for SQL window functions (ROW_NUMBER, RANK, DENSE_RANK, NTILE) with no EF Core dependency. Pair with ExpressiveSharp.EntityFrameworkCore.RelationalExtensions for EF Core SQL translation. + + net8.0;net9.0;net10.0 diff --git a/src/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.csproj b/src/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.csproj index a59f1e22..d1b5effd 100644 --- a/src/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.csproj +++ b/src/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions.csproj @@ -2,8 +2,11 @@ Relational database extensions for ExpressiveSharp EF Core — SQL window functions (ranking, aggregate, navigation) with PARTITION BY / ORDER BY / ROWS|RANGE frame support, plus indexed Select. - - $(NoWarn);NU5104 + + net8.0;net9.0;net10.0 true true + + + true + + + + + $(DefineConstants);TEST_SQLSERVER @@ -107,8 +116,10 @@ - - + + diff --git a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/TestContextFactories.cs b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/TestContextFactories.cs index 8fa4d84f..539da4ac 100644 --- a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/TestContextFactories.cs +++ b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/TestContextFactories.cs @@ -33,7 +33,12 @@ public static SqliteContextHandle CreateSqlite( var options = new DbContextOptionsBuilder() .UseSqlite(connection) + // RelationalExtensions does not target net11.0 (dotnet/efcore#38977). +#if NET11_0_OR_GREATER + .UseExpressives() +#else .UseExpressives(o => o.UseRelationalExtensions()) +#endif .Options; return new SqliteContextHandle(factory(options), connection); @@ -72,7 +77,11 @@ public static SqlServerContextHandle CreateSqlServer( var options = new DbContextOptionsBuilder() .UseSqlServer(connStr) +#if NET11_0_OR_GREATER + .UseExpressives() +#else .UseExpressives(o => o.UseRelationalExtensions()) +#endif .Options; return new SqlServerContextHandle(factory(options)); diff --git a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/WindowFunctionTestBase.cs b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/WindowFunctionTestBase.cs index 78b55adf..309569e7 100644 --- a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/WindowFunctionTestBase.cs +++ b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/WindowFunctionTestBase.cs @@ -57,9 +57,6 @@ public async Task RowNumber_ReturnsCorrectSequentialNumbers() } } - // All window functions except RowNumber are unsupported on EF Core 11 — the translator - // throws NotSupportedException (see WindowFunctionMethodCallTranslator). -#if !NET11_0_OR_GREATER [TestMethod] public async Task Rank_WithTies_ReturnsGaps() { @@ -138,7 +135,6 @@ public async Task Ntile_DistributesIntoBuckets() Assert.AreEqual(2, bucketCounts[2]); Assert.AreEqual(2, bucketCounts[3]); } -#endif [TestMethod] public async Task RowNumber_WithPartitionBy_ResetsPerGroup() @@ -183,7 +179,6 @@ public async Task IndexedSelect_ReturnsZeroBasedIndices() CollectionAssert.AreEqual(new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, positions); } -#if !NET11_0_OR_GREATER [TestMethod] public async Task MultipleWindowFunctions_InSameSelect() { @@ -208,7 +203,6 @@ public async Task MultipleWindowFunctions_InSameSelect() Assert.AreEqual(9, results[9].Dense); Assert.AreEqual(4, results[9].Quartile); } -#endif // RowNumber over o.Total (which is [Expressive] => Price * Quantity). The // ExpressiveQueryCompiler must expand Total inside the OrderBy argument of @@ -252,7 +246,6 @@ public async Task RowNumber_OverExpressiveTotal_UsesExpandedExpression() Assert.IsTrue(results[i].Total >= results[i - 1].Total); } -#if !NET11_0_OR_GREATER // Aggregate window functions (SUM, AVG, COUNT, MIN, MAX) depend on the frame clause. // Seed prices (ASC): 10, 15, 20, 20, 25, 30, 35, 40, 45, 50 // Running total: 10, 25, 45, 65, 90, 120, 155, 195, 240, 290 @@ -970,7 +963,6 @@ public async Task LastValue_DefaultFrame_ReturnsCurrentRow() Assert.AreEqual(r.Price, r.Last, $"With the default frame, LAST_VALUE should return the current row's price (got {r.Last} vs {r.Price})"); } -#endif [TestMethod] public void Ntile_ZeroBuckets_ThrowsAtTranslation() @@ -1040,19 +1032,4 @@ public void NthValue_ZeroPosition_ThrowsAtTranslation() StringAssert.Contains(ex.Message, "NthValue"); StringAssert.Contains(ex.Message, "1-based"); } - -#if NET11_0_OR_GREATER - [TestMethod] - public void WindowFunction_ThrowsNotSupported_OnEfCore11() - { - var query = Context.Orders.Select(o => new - { - o.Id, - PriceRank = WindowFunction.Rank(Window.OrderBy(o.Price)), - }); - - var ex = Assert.ThrowsExactly(() => query.ToQueryString()); - StringAssert.Contains(ex.Message, "EF Core 11"); - } -#endif } diff --git a/tests/ExpressiveSharp.Tests/ExpressiveSharp.Tests.csproj b/tests/ExpressiveSharp.Tests/ExpressiveSharp.Tests.csproj index 404e04c2..f2f31de2 100644 --- a/tests/ExpressiveSharp.Tests/ExpressiveSharp.Tests.csproj +++ b/tests/ExpressiveSharp.Tests/ExpressiveSharp.Tests.csproj @@ -3,12 +3,22 @@ false true + + + true + + + + - - + + From c6f2367e1423dd46090c05250355d816c4334d95 Mon Sep 17 00:00:00 2001 From: Koen <2912652+koenbeuk@users.noreply.github.com> Date: Sun, 20 Sep 2026 02:15:27 +0000 Subject: [PATCH 3/6] Fix CI: .NET 11 RC dotnet test mis-resolves relative --project paths The .NET 11 RC SDK's dotnet test (Microsoft.Testing.Platform runner) resolves a relative --project path against the project's own directory, doubling it (tests/X/tests/X/X.csproj) and failing before any test runs. Use absolute paths in ci.yml and test-containers.sh; verified the exact invocation (incl. --report-trx/--coverage) against 11.0.100-rc.1 locally. Co-Authored-By: Claude Fable 5 --- .github/workflows/ci.yml | 10 ++++++---- test-containers.sh | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ca234f7..e5f1ad59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,13 +60,15 @@ jobs: # Docker or pay the container startup cost on every PR. run: | set -e + # --project must be absolute: the .NET 11 RC SDK's dotnet test resolves + # relative --project paths against the project directory, doubling them. for proj in \ tests/ExpressiveSharp.Tests \ tests/ExpressiveSharp.IntegrationTests \ tests/ExpressiveSharp.Generator.Tests \ tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests; do dotnet test --no-build -c Release \ - --project "$proj" \ + --project "$GITHUB_WORKSPACE/$proj" \ --results-directory ./test-results \ -- \ --report-trx --report-trx-filename "$(basename "$proj").trx" \ @@ -184,7 +186,7 @@ jobs: for tfm in net8.0 net9.0 net10.0 net11.0; do echo "::group::Cosmos tests ($tfm)" dotnet test --no-build -c Release \ - --project tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests \ + --project "$GITHUB_WORKSPACE/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests" \ -p:TestDatabase=${{ matrix.database }} \ -f "$tfm" \ --results-directory ./test-results \ @@ -194,7 +196,7 @@ jobs: done else dotnet test --no-build -c Release \ - --project tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests \ + --project "$GITHUB_WORKSPACE/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests" \ -p:TestDatabase=${{ matrix.database }} \ --results-directory ./test-results \ -- \ @@ -269,7 +271,7 @@ jobs: - name: Test run: >- dotnet test --no-build -c Release - --project tests/ExpressiveSharp.MongoDB.IntegrationTests + --project "$GITHUB_WORKSPACE/tests/ExpressiveSharp.MongoDB.IntegrationTests" --results-directory ./test-results -- --report-trx --report-trx-filename results.trx diff --git a/test-containers.sh b/test-containers.sh index f17a3a25..e48d8574 100755 --- a/test-containers.sh +++ b/test-containers.sh @@ -30,6 +30,8 @@ echo "==> Building with TestDatabase=$database..." dotnet build "$PROJECT" -c "$CONFIG" -p:TestDatabase="$database" --verbosity quiet echo "==> Running tests with TestDatabase=$database..." -dotnet test --project tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests \ +# --project must be absolute: the .NET 11 RC SDK's dotnet test resolves +# relative --project paths against the project directory, doubling them. +dotnet test --project "$(cd "$(dirname "$0")" && pwd)/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests" \ --no-build -c "$CONFIG" \ -p:TestDatabase="$database" From c39904aacf4c5d3a3a756e610f78959745ae4064 Mon Sep 17 00:00:00 2001 From: Koen <2912652+koenbeuk@users.noreply.github.com> Date: Sun, 20 Sep 2026 02:31:41 +0000 Subject: [PATCH 4/6] Mark EF Core 11 Cosmos shaper regressions Inconclusive on net11.0 Six CommonScenario projection tests fail on the Cosmos provider only on net11.0, all reproduced with vanilla EF Core 11.0.0-rc.1 (no ExpressiveSharp involved): constructor and interpolated-string projections throw "A part of the projection was undefined" when they include a JSON-null value or mix the key property with a coalesced member, and string-concat projections of int properties fail shaper compilation with an invalid int -> string Convert (ShaperProcessingExpressionVisitor.ReadJsonValue). Root cause is EF 11's rewritten System.Text.Json Cosmos shaper; the same queries pass on EF Core 8/9/10 Cosmos and every relational provider. Follow the existing Cosmos convention: override the affected tests with Assert.Inconclusive under #if NET11_0_OR_GREATER (base methods made virtual). Revisit at EF 11 GA. Co-Authored-By: Claude Fable 5 --- .../Infrastructure/CommonScenarioTestBase.cs | 12 ++--- .../Tests/Cosmos/CommonScenarioTests.cs | 44 +++++++++++++++++++ 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/CommonScenarioTestBase.cs b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/CommonScenarioTestBase.cs index 4c9a3f65..3c0bb46e 100644 --- a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/CommonScenarioTestBase.cs +++ b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/CommonScenarioTestBase.cs @@ -137,7 +137,7 @@ public virtual async Task Select_PriceBreakpoints_ReturnsArrayLiteral() } [TestMethod] - public async Task Select_OrderDto_ProjectsCorrectly() + public virtual async Task Select_OrderDto_ProjectsCorrectly() { Expression> expr = o => new OrderDto(o.Id, o.Tag ?? "N/A", o.Total); var expanded = (Expression>)expr.ExpandExpressives(); @@ -519,7 +519,7 @@ public async Task Polyfill_NullCoalescing_ProjectsCorrectly() } [TestMethod] - public async Task Select_Summary_ReturnsCorrectValues() + public virtual async Task Select_Summary_ReturnsCorrectValues() { Expression> expr = o => o.Summary; var expanded = (Expression>)expr.ExpandExpressives(); @@ -538,7 +538,7 @@ public async Task Select_Summary_ReturnsCorrectValues() } [TestMethod] - public async Task Select_SummaryConcat_ReturnsCorrectValues() + public virtual async Task Select_SummaryConcat_ReturnsCorrectValues() { Expression> expr = o => o.SummaryConcat; var expanded = (Expression>)expr.ExpandExpressives(); @@ -699,7 +699,7 @@ public virtual async Task Where_IsPriceQuantityMatch_FiltersCorrectly() } [TestMethod] - public async Task Select_InlineTuple_ProjectsCorrectly() + public virtual async Task Select_InlineTuple_ProjectsCorrectly() { var expr = ExpressionPolyfill.Create((Order o) => (o.Id, o.Price)); @@ -711,7 +711,7 @@ public async Task Select_InlineTuple_ProjectsCorrectly() } [TestMethod] - public async Task Select_TupleWithExpressiveMember_ProjectsCorrectly() + public virtual async Task Select_TupleWithExpressiveMember_ProjectsCorrectly() { var expr = ExpressionPolyfill.Create((Order o) => (o.Id, o.Price * o.Quantity)); @@ -724,7 +724,7 @@ public async Task Select_TupleWithExpressiveMember_ProjectsCorrectly() } [TestMethod] - public async Task Select_TupleWithNullable_ProjectsCorrectly() + public virtual async Task Select_TupleWithNullable_ProjectsCorrectly() { var expr = ExpressionPolyfill.Create((Order o) => (o.Id, o.Tag ?? "none")); diff --git a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Tests/Cosmos/CommonScenarioTests.cs b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Tests/Cosmos/CommonScenarioTests.cs index d0e236de..8eec953d 100644 --- a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Tests/Cosmos/CommonScenarioTests.cs +++ b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Tests/Cosmos/CommonScenarioTests.cs @@ -242,5 +242,49 @@ public override Task Select_CustomerCountry_TwoLevelChain() Assert.Inconclusive("Cosmos DB drops null rows in null-conditional projections"); return Task.CompletedTask; } + +#if NET11_0_OR_GREATER + // EF Core 11 rc.1 Cosmos regressions in the rewritten System.Text.Json shaper, + // reproduced with vanilla EF Core (no ExpressiveSharp involved): constructor and + // interpolated-string projections throw "A part of the projection was undefined" + // when they include a JSON-null value or mix the key property with a coalesced + // member, and string-concat projections of int properties fail shaper compilation + // with an invalid int -> string Convert. Revisit at EF 11 GA. + public override Task Select_OrderDto_ProjectsCorrectly() + { + Assert.Inconclusive("EF Core 11 rc.1 Cosmos shaper regression"); + return Task.CompletedTask; + } + + public override Task Select_Summary_ReturnsCorrectValues() + { + Assert.Inconclusive("EF Core 11 rc.1 Cosmos shaper regression"); + return Task.CompletedTask; + } + + public override Task Select_SummaryConcat_ReturnsCorrectValues() + { + Assert.Inconclusive("EF Core 11 rc.1 Cosmos shaper regression"); + return Task.CompletedTask; + } + + public override Task Select_InlineTuple_ProjectsCorrectly() + { + Assert.Inconclusive("EF Core 11 rc.1 Cosmos shaper regression"); + return Task.CompletedTask; + } + + public override Task Select_TupleWithExpressiveMember_ProjectsCorrectly() + { + Assert.Inconclusive("EF Core 11 rc.1 Cosmos shaper regression"); + return Task.CompletedTask; + } + + public override Task Select_TupleWithNullable_ProjectsCorrectly() + { + Assert.Inconclusive("EF Core 11 rc.1 Cosmos shaper regression"); + return Task.CompletedTask; + } +#endif } #endif From c19fdcc11b7d8435771df1bd97f1ab9695498e3b Mon Sep 17 00:00:00 2001 From: Koen <2912652+koenbeuk@users.noreply.github.com> Date: Sun, 20 Sep 2026 02:48:34 +0000 Subject: [PATCH 5/6] Docs: state EF Core 11 unavailability of RelationalExtensions everywhere users land The full explanation lived only in the window-functions guide. Add a warning to the window-functions recipe (self-contained setup instructions), an "EF Core 11 is not supported" subsection to the limitations page (including the NuGet nearest-TFM fallback pitfall), a note in the EF Core integration guide's ExecuteUpdate section, and "(EF Core 8-10)" qualifiers to the package tables in README, quickstart, and the docs landing page. Co-Authored-By: Claude Fable 5 --- README.md | 2 +- docs/advanced/limitations.md | 4 ++++ docs/guide/integrations/ef-core.md | 4 ++++ docs/guide/quickstart.md | 2 +- docs/index.md | 4 ++-- docs/recipes/window-functions-ranking.md | 4 ++++ 6 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ac253a0c..6f07c7e0 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ Mark computed properties and methods with `[Expressive]` to generate companion e | External member mapping | `[ExpressiveFor]` for BCL/third-party members | | Tuples, index/range, `with`, collection expressions | And more modern C# syntax | | Expression transformers | Built-in + custom `IExpressionTreeTransformer` pipeline | -| SQL window functions | ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST, SUM/AVG/COUNT/MIN/MAX OVER, LAG/LEAD, FIRST_VALUE/LAST_VALUE/NTH_VALUE with ROWS/RANGE frames | +| SQL window functions | ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST, SUM/AVG/COUNT/MIN/MAX OVER, LAG/LEAD, FIRST_VALUE/LAST_VALUE/NTH_VALUE with ROWS/RANGE frames (EF Core 8–10) | | Hot reload | Compatible with `dotnet watch` — edits to `[Expressive]` bodies propagate to generated expression trees | See the [full documentation](https://efnext.github.io/ExpressiveSharp/guide/introduction) for detailed usage, [reference](https://efnext.github.io/ExpressiveSharp/reference/expressive-attribute), and [recipes](https://efnext.github.io/ExpressiveSharp/recipes/computed-properties). diff --git a/docs/advanced/limitations.md b/docs/advanced/limitations.md index 9be275ff..e22164b8 100644 --- a/docs/advanced/limitations.md +++ b/docs/advanced/limitations.md @@ -227,4 +227,8 @@ The `ExpressiveSharp.EntityFrameworkCore.RelationalExtensions` package implement Non-relational providers (Cosmos DB, in-memory) are not supported for window functions. +### EF Core 11 is not supported + +The `RelationalExtensions` packages support EF Core 8, 9, and 10 but do not ship a `net11.0` target: EF Core 11 removed the `QuerySqlGenerator` fallback that renders third-party SQL expressions ([dotnet/efcore#37533](https://github.com/dotnet/efcore/pull/37533)), with no replacement extension point. Support returns once EF Core provides one — tracked in [dotnet/efcore#38977](https://github.com/dotnet/efcore/issues/38977). Applications on .NET 11 that need window functions should stay on EF Core 10, which runs fine on the .NET 11 runtime. Note that NuGet will not block installing the package in a `net11.0` project — it silently falls back to the `net10.0` build, and window functions then fail at runtime with EF Core 11's `Unhandled expression … in 'QuerySqlGenerator'` error. + EF Core also tracks native window function support in [dotnet/efcore#12747](https://github.com/dotnet/efcore/issues/12747); see the [window functions guide](../guide/window-functions#forward-compatibility) for forward-compatibility notes. diff --git a/docs/guide/integrations/ef-core.md b/docs/guide/integrations/ef-core.md index 5af1ca93..fc99f9d9 100644 --- a/docs/guide/integrations/ef-core.md +++ b/docs/guide/integrations/ef-core.md @@ -149,6 +149,10 @@ Supported chain-preserving operations: With the `ExpressiveSharp.EntityFrameworkCore.RelationalExtensions` package, you can use modern C# syntax inside `ExecuteUpdate` / `ExecuteUpdateAsync`: +::: warning +The `RelationalExtensions` package supports EF Core 8–10 only; it does not ship a `net11.0` target until EF Core 11 restores an extension point for third-party SQL expressions ([dotnet/efcore#38977](https://github.com/dotnet/efcore/issues/38977)). See the [window functions guide](../window-functions) for details. +::: + ```csharp // Requires: .UseExpressives(o => o.UseRelationalExtensions()) ctx.Orders diff --git a/docs/guide/quickstart.md b/docs/guide/quickstart.md index c93ca0f6..6059c177 100644 --- a/docs/guide/quickstart.md +++ b/docs/guide/quickstart.md @@ -39,7 +39,7 @@ dotnet add package ExpressiveSharp.MongoDB | `ExpressiveSharp.Abstractions` | Lightweight — `[Expressive]` attribute, `[ExpressiveFor]`, `IExpressionTreeTransformer`, source generator only (no runtime services) | | `ExpressiveSharp.EntityFrameworkCore` | EF Core integration — `UseExpressives()`, `ExpressiveDbSet`, Include/ThenInclude, async methods, analyzers and code fixes | | `ExpressiveSharp.MongoDB` | MongoDB integration — `.AsExpressive()` on `IMongoCollection`, MQL aggregation translation | -| `ExpressiveSharp.EntityFrameworkCore.RelationalExtensions` | SQL window functions — ranking (ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST), aggregate (SUM, AVG, COUNT, MIN, MAX), and navigation (LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE) with PARTITION BY / ORDER BY / ROWS\|RANGE frame support, plus indexed Select. | +| `ExpressiveSharp.EntityFrameworkCore.RelationalExtensions` | SQL window functions (EF Core 8–10; [EF Core 11 pending](https://github.com/dotnet/efcore/issues/38977)) — ranking (ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST), aggregate (SUM, AVG, COUNT, MIN, MAX), and navigation (LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE) with PARTITION BY / ORDER BY / ROWS\|RANGE frame support, plus indexed Select. | ## Step 2 — Define Your Entities diff --git a/docs/index.md b/docs/index.md index 791d3c59..43b797dd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -47,7 +47,7 @@ features: - icon: "\U0001F4CA" title: SQL Window Functions - details: "Ranking (ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST), aggregate (SUM, AVG, COUNT, MIN, MAX), and navigation (LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE) functions with a fluent PARTITION BY / ORDER BY / frame API — via the RelationalExtensions package." + details: "Ranking (ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST), aggregate (SUM, AVG, COUNT, MIN, MAX), and navigation (LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE) functions with a fluent PARTITION BY / ORDER BY / frame API — via the RelationalExtensions package (EF Core 8–10)." - icon: "\U0001F527" title: Customizable Transformer Pipeline @@ -116,4 +116,4 @@ Computed properties are **inlined into the provider's native query language** | [`ExpressiveSharp.Abstractions`](https://www.nuget.org/packages/ExpressiveSharp.Abstractions/) | Lightweight — attributes (`[Expressive]`, `[ExpressiveFor]`), `IExpressionTreeTransformer`, source generator only | | [`ExpressiveSharp.EntityFrameworkCore`](https://www.nuget.org/packages/ExpressiveSharp.EntityFrameworkCore/) | EF Core integration — `UseExpressives()`, `ExpressiveDbSet`, Include/ThenInclude, async methods | | [`ExpressiveSharp.MongoDB`](https://www.nuget.org/packages/ExpressiveSharp.MongoDB/) | MongoDB integration — `.AsExpressive()` on `IMongoCollection`, MQL translation | -| [`ExpressiveSharp.EntityFrameworkCore.RelationalExtensions`](https://www.nuget.org/packages/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions/) | SQL window functions — ranking, aggregate, navigation with ROWS/RANGE frames | +| [`ExpressiveSharp.EntityFrameworkCore.RelationalExtensions`](https://www.nuget.org/packages/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions/) | SQL window functions — ranking, aggregate, navigation with ROWS/RANGE frames (EF Core 8–10) | diff --git a/docs/recipes/window-functions-ranking.md b/docs/recipes/window-functions-ranking.md index 1136f982..30b29c77 100644 --- a/docs/recipes/window-functions-ranking.md +++ b/docs/recipes/window-functions-ranking.md @@ -2,6 +2,10 @@ This recipe shows how to use SQL window functions in EF Core LINQ queries via the `ExpressiveSharp.EntityFrameworkCore.RelationalExtensions` package. Coverage includes ranking (ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST), aggregate (SUM, AVG, COUNT, MIN, MAX), and navigation (LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE) functions. +::: warning Not supported on EF Core 11 +The `RelationalExtensions` packages do not ship a `net11.0` target — EF Core 11 removed the SQL-generation fallback the window function translation relies on. See the [window functions guide](../guide/window-functions) for details and [dotnet/efcore#38977](https://github.com/dotnet/efcore/issues/38977) for tracking. On .NET 11, stay on EF Core 10. +::: + ## Setup Install the package: From 68d41ab77bf91edb123cca05af5515544a9adebb Mon Sep 17 00:00:00 2001 From: Koen <2912652+koenbeuk@users.noreply.github.com> Date: Sun, 20 Sep 2026 03:13:04 +0000 Subject: [PATCH 6/6] Address Copilot review: TFM gates are allow-lists, not deny-lists NpgsqlSupported and RelationalExtensionsSupported used != 'net11.0', so a future TFM would silently enable them without matching package references. Enumerate the supported TFMs explicitly, matching the PomeloMySqlSupported convention. Co-Authored-By: Claude Fable 5 --- ...xpressiveSharp.EntityFrameworkCore.IntegrationTests.csproj | 4 ++-- tests/ExpressiveSharp.Tests/ExpressiveSharp.Tests.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests.csproj b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests.csproj index 3dd8da6c..297dd4e1 100644 --- a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests.csproj +++ b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests.csproj @@ -14,11 +14,11 @@ true - true + true - true + true diff --git a/tests/ExpressiveSharp.Tests/ExpressiveSharp.Tests.csproj b/tests/ExpressiveSharp.Tests/ExpressiveSharp.Tests.csproj index f2f31de2..87044cfa 100644 --- a/tests/ExpressiveSharp.Tests/ExpressiveSharp.Tests.csproj +++ b/tests/ExpressiveSharp.Tests/ExpressiveSharp.Tests.csproj @@ -6,7 +6,7 @@ - true + true