Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {},
Expand Down
39 changes: 34 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -51,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" \
Expand Down Expand Up @@ -135,6 +146,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:
Expand Down Expand Up @@ -163,10 +183,10 @@ 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 \
--project "$GITHUB_WORKSPACE/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests" \
-p:TestDatabase=${{ matrix.database }} \
-f "$tfm" \
--results-directory ./test-results \
Expand All @@ -176,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 \
-- \
Expand Down Expand Up @@ -220,6 +240,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:
Expand All @@ -242,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
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
26 changes: 16 additions & 10 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -67,50 +67,56 @@ 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<T>, ExpressionPolyfill, .ExpandExpressives(), .AsExpressive()

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<T>, ExpressiveMongoCollection<T>,
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<T>, IIncludableExpressiveQueryable<T,P>,
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 — 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;
aggregate: SUM, AVG, COUNT, MIN, MAX; navigation: LAG, LEAD, FIRST_VALUE,
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 — no net11.0, see below)
├── 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 (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 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
Expand Down Expand Up @@ -145,7 +151,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
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0;net10.0;net11.0</TargetFrameworks>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<LangVersion>14.0</LangVersion>
<Nullable>enable</Nullable>
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.3" />
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="8.23.60" />
<PackageVersion Include="Basic.Reference.Assemblies.Net90" Version="1.8.4" />
<PackageVersion Include="Basic.Reference.Assemblies.Net110" Version="1.8.11" />
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
<PackageVersion Include="Testcontainers" Version="4.3.0" />
<PackageVersion Include="Testcontainers.MsSql" Version="4.3.0" />
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
4 changes: 4 additions & 0 deletions docs/advanced/limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 1 addition & 1 deletion docs/advanced/testing-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions docs/guide/integrations/ef-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions docs/guide/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>`**.

## Step 1 — Install the Packages
Expand Down Expand Up @@ -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<T>`, Include/ThenInclude, async methods, analyzers and code fixes |
| `ExpressiveSharp.MongoDB` | MongoDB integration — `.AsExpressive()` on `IMongoCollection<T>`, 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

Expand Down
4 changes: 4 additions & 0 deletions docs/guide/window-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. 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

```bash
Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<T>`, Include/ThenInclude, async methods |
| [`ExpressiveSharp.MongoDB`](https://www.nuget.org/packages/ExpressiveSharp.MongoDB/) | MongoDB integration — `.AsExpressive()` on `IMongoCollection<T>`, 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) |
4 changes: 4 additions & 0 deletions docs/recipes/window-functions-ranking.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions samples/EFCoreSample/EFCoreSample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite"
Condition="'$(TargetFramework)' == 'net10.0'"
VersionOverride="10.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite"
Condition="'$(TargetFramework)' == 'net11.0'"
VersionOverride="11.0.0-rc.1.26425.128" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

<PropertyGroup>
<Description>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.</Description>
<!-- No net11.0 until RelationalExtensions supports EF Core 11 (dotnet/efcore#38977). -->
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
</PropertyGroup>

</Project>
Loading
Loading