Repository navigation
Description
Problem
dotnet/efcore#39114 ("[release/10.0] Fix Contains handling for read-only collections") merged into release/10.0 on 2026-10-02 and is not in EF Core 10.0.13, the newest patch as of 2026-10-19. The patch that carries it makes EF Core 10 normalize every Contains on an IReadOnlyCollection<T> into Queryable.Contains over the element type, which is TRange for a RangeSet<TRange, T>. On that patch every RangeSet.Contains(T) and Contains(RangeSet) in a query throws ArgumentException inside EF Core, before any translator runs — the failure the 11 line hit on EF Core 11 RC1 (see the 11.0.0 issue, fixed on main by the #103 PR).
The repository's own test runs pin EF Core 10.0.0 through Npgsql 10.0.0's floor, so CI on release/10.x does not see it; a consumer on the newer patch does. Loud, not silent.
Change
Cherry-pick the main fix (RangeSetContains, the interceptor rewrite and the translator case) with -x into a PR against release/10.x, and make the EF Core translation tests run on the patch that carries the change once it is published, so the line's CI sees what its consumers see.
Done when
Measurable now, without waiting for EF Core 10.0.15: on release/10.x the interceptor rewrites all three RangeSet.Contains overloads onto RangeSetContains, a translation test asserts that no instance Contains call whose declaring type implements IReadOnlyCollection<> survives interception (BCL and NodaTime range sets alike), the @> SQL of every existing RangeSet.Contains test is unchanged, and reverting the rewrite fails that test while the SQL tests still pass — which is why the defect was invisible. Ships in 10.1.0 (a minor; the fix is loud, the minor is for #300).
Re-check, not a gate: when EF Core 10.0.15 is published, run the EF suites on it through the #297 leg (or the scratch float described there) and record the result in a comment here.
Activity
CaffeinatedCoder commentedon Oct 19, 2026
Root cause: EF Core's
Containsnormalization, exposed byRangeSetbeing a collectionThe defect is in EF Core.
QueryableMethodNormalizingExpressionVisitorrewrites a call intoQueryable.Containswhen the method is namedContains, its declaring type is generic, and that type implementsICollection<>— and, since dotnet/efcore#37390 (backported torelease/10.0as dotnet/efcore#39114, issue dotnet/efcore#37081), alsoIReadOnlyCollection<>.TryConvertCollectionContainsToQueryableContainsthen builds:Two things are wrong with it, independent of this package:
Contains(TElement). Any one-argument overload namedContainsis rewritten, so an operand that is not the element type fails inExpression.CallwithArgumentException. That isRangeSet<TRange, T>.Contains(T)andContains(RangeSet).TryGetElementTypefound. ForRangeSet<TRange, T>that guess happens to be right; forFoo<TKey, TItem> : ICollection<TItem>it is not.Both already reproduce on EF Core 10.0.13 through
ICollection<>(a scratch repro with a genericICollection<Span>that has aContains(int)overload, and aKeyed<TKey, TItem> : ICollection<TItem>; both throw fromTryConvertCollectionContainsToQueryableContains). The patch adds no new defect; it widens the existing one toIReadOnlyCollection<>, which is whereRangeSetsits (IReadOnlyList<TRange>). EF Coremain(the 11 line) has the same code. An upstream report is being drafted.What this package contributes is the exposure:
RangeSetis a read-only list of its ranges whoseContainsoverloads take operands that are not ranges. The same reason value sets never implementIEnumerable<T>.Consequences for this issue
mainfix moves the call onto a declaring type that is not a collection (RangeSetContains), which EF Core's check does not match, and it keeps working whether or not EF Core narrows the check later.ICollection<>), soRangeSet.Containstranslates again; the cost is losing EF Core's own fix, so a capturedIReadOnlySet<T>inlines its values instead of going out as one collection parameter. Worth a line in the 10.x changelog entry for this fix.CaffeinatedCoder commentedon Oct 19, 2026
Filed upstream as dotnet/efcore#39329. The fix here doesn't wait on it:
RangeSetContainstakes the call off a collection's declaring type, so it holds whether or not EF Core narrows the check.CaffeinatedCoder commentedon Oct 19, 2026
Measured 2026-10-19 from the upstream PR and the published packages:
Microsoft.EntityFrameworkCore.Issue37081is in none of 10.0.0 through 10.0.13.Microsoft.EntityFrameworkCore [10.0.4, 11.0.0), so every consumer on a current Npgsql EF already resolves an EF Core patch above the floor this repository tests on. When 10.0.15 ships, a plaindotnet restorewith floating patch versions, or a Dependabot bump, is enough to hit this.mainfix lives in PR build: retarget main to .NET 11 for 11.0.0-preview.1 #292 (231f23a), which is a single commit holding the whole .NET 11 retarget, so the 10.x port is a hand-extracted commit that names the PR, not acherry-pick -xof it, unless build: retarget main to .NET 11 for 11.0.0-preview.1 #292 is split before merge.CaffeinatedCoder commentedon Oct 19, 2026
Port against
release/10.xis #310 (branchfix/288-rangeset-contains-ef-core-patches, one hand-extracted commit naming PR #292). Since no published EF Core 10 patch carries dotnet/efcore#39114 yet and the dncengdotnet10feed lists no 10.0.x servicing build (itsMicrosoft.EntityFrameworkCorelisting ends at10.0.0-rtm.25513.102), the PR proves the fix on the query tree: a translation test per overload, on the BCL and NodaTime range sets, asserts no instanceContainson anIReadOnlyCollection<>survives interception, and fails with the rewrite stripped while every@>SQL test passes. Closed by hand after the merge.fix: keep RangeSet.Contains out of EF Core's rewrite of Contains on a…
CaffeinatedCoder commentedon Oct 19, 2026
Fixed on
release/10.xby PR #310 (merge commit 9e4cce4, 2026-10-19): the interceptor moves the threeRangeSet.Containsoverloads ontoRangeSetContains, proved through the interception tree since no published EF Core 10 patch carries dotnet/efcore#39114 yet. Re-check against EF Core 10.0.15 when it ships (#297's leg). Closed by hand:Closesacts only onmain, where the fix arrived with PR #292 (#287).