Skip to content

10.x: RangeSet.Contains throws ArgumentException on the EF Core 10 patch that carries dotnet/efcore#37081 #288

Closed

Description

@CaffeinatedCoder

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

  1. added
    bugSomething isn't working
    area/range-setRangeSet and multirange algebra
    package/efcoreAffects CodoMetis.ValueRanges.EFCore.PostgreSQL
    package/efcore-nodatimeAffects CodoMetis.ValueRanges.EFCore.PostgreSQL.NodaTime
    severity/highWrong results, crash or failed writes in a realistic scenario
    on Oct 19, 2026
  2. CaffeinatedCoder commented on Oct 19, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Root cause: EF Core's Contains normalization, exposed by RangeSet being a collection

    The defect is in EF Core. QueryableMethodNormalizingExpressionVisitor rewrites a call into Queryable.Contains when the method is named Contains, its declaring type is generic, and that type implements ICollection<> — and, since dotnet/efcore#37390 (backported to release/10.0 as dotnet/efcore#39114, issue dotnet/efcore#37081), also IReadOnlyCollection<>. TryConvertCollectionContainsToQueryableContains then builds:

    var sourceType = methodCallExpression.Method.DeclaringType!.GetGenericArguments()[0];
    Expression.Call(QueryableMethods.Contains.MakeGenericMethod(sourceType), AsQueryable<sourceType>(obj), methodCallExpression.Arguments[0]);

    Two things are wrong with it, independent of this package:

    1. It never checks the method is the collection's Contains(TElement). Any one-argument overload named Contains is rewritten, so an operand that is not the element type fails in Expression.Call with ArgumentException. That is RangeSet<TRange, T>.Contains(T) and Contains(RangeSet).
    2. It takes the element type as the declaring type's first type argument, not the element type TryGetElementType found. For RangeSet<TRange, T> that guess happens to be right; for Foo<TKey, TItem> : ICollection<TItem> it is not.

    Both already reproduce on EF Core 10.0.13 through ICollection<> (a scratch repro with a generic ICollection<Span> that has a Contains(int) overload, and a Keyed<TKey, TItem> : ICollection<TItem>; both throw from TryConvertCollectionContainsToQueryableContains). The patch adds no new defect; it widens the existing one to IReadOnlyCollection<>, which is where RangeSet sits (IReadOnlyList<TRange>). EF Core main (the 11 line) has the same code. An upstream report is being drafted.

    What this package contributes is the exposure: RangeSet is a read-only list of its ranges whose Contains overloads take operands that are not ranges. The same reason value sets never implement IEnumerable<T>.

    Consequences for this issue

  3. CaffeinatedCoder commented on Oct 19, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Filed upstream as dotnet/efcore#39329. The fix here doesn't wait on it: RangeSetContains takes the call off a collection's declaring type, so it holds whether or not EF Core narrows the check.

  4. added this to the 10.0.1 milestone on Oct 19, 2026
  5. CaffeinatedCoder commented on Oct 19, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Measured 2026-10-19 from the upstream PR and the published packages:

  6. CaffeinatedCoder commented on Oct 19, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Port against release/10.x is #310 (branch fix/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 dnceng dotnet10 feed lists no 10.0.x servicing build (its Microsoft.EntityFrameworkCore listing ends at 10.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 instance Contains on an IReadOnlyCollection<> survives interception, and fails with the rewrite stripped while every @> SQL test passes. Closed by hand after the merge.

  7. CaffeinatedCoder commented on Oct 19, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Fixed on release/10.x by PR #310 (merge commit 9e4cce4, 2026-10-19): the interceptor moves the three RangeSet.Contains overloads onto RangeSetContains, 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: Closes acts only on main, where the fix arrived with PR #292 (#287).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/range-setRangeSet and multirange algebrabugSomething isn't workingpackage/efcoreAffects CodoMetis.ValueRanges.EFCore.PostgreSQLpackage/efcore-nodatimeAffects CodoMetis.ValueRanges.EFCore.PostgreSQL.NodaTimeseverity/highWrong results, crash or failed writes in a realistic scenario

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions