Skip to content

Any/Count over an empty captured collection of value sets (or of ranges beside Contains) throws 'Empty collections are not supported as inline query roots' #137

Closed

Description

@CaffeinatedCoder

Any or Count with a predicate over an empty captured collection throws when EF Core translates the query. Memory answers false or 0. It affects every value set type, wherever the collection is used, and every range type when a Contains in the same query reads the collection. Measured on main (86a59a9) against PostgreSQL 18 by CapturedCollectionProbeTests (#136):

var wanted = new List<StringSet>();
db.Items.Where(r => wanted.Any(w => w.Overlaps(r.Tags)));     // InvalidOperationException: Empty collections are not supported as inline query roots.
db.Items.Select(r => wanted.Count(w => w.IsSubsetOf(r.Tags))); // the same

var ranges = new List<Int32Range>();
db.Reservations.Where(r => ranges.Contains(r.Seats) || ranges.Any(w => w.Overlaps(r.Seats))); // the same

Scope: every value set type (closed, wrapper and NodaTime), every range type beside a Contains, every collection form (array, List, HashSet, IEnumerable), and every predicate the sweep asks (Overlaps, IsSubsetOf, IsEmpty, IsAdjacentTo). Three cases don't fail: Contains (EF Core folds IN () to false), Any(w => w == c) (rewritten to IN), and Count() without a predicate.

Cause: for a captured collection of value sets, or of ranges that a Contains reads, the interceptor switches the parameter to EF Core's ParameterTranslationMode.MultipleParameters (#75, #77). In that mode a collection used as a query root is written as VALUES (@p1), (@p2), …, and EF Core refuses to write VALUES with no rows. Before 9.1.0 these queries failed too, with InvalidCastException (value sets) or 42809 (ranges beside Contains), so nothing that worked broke. But the error doesn't say what to write instead.

Why it matters for #76: a range collection that no Contains reads still goes out as one multirange parameter, and over an empty list that happens to answer correctly (unnest of an empty multirange). #76's fix switches those collections too, so as written it would turn the empty case from a correct answer into this exception. The two need to be fixed together.

Fix (10.0.0): for an empty collection, translate Any(pred) as false and Count(pred) as 0. Or keep the collection as a typed array parameter when it has no elements ('{}'::int4range[] is a valid empty array of ranges, and an empty array of arrays can be typed too). Or at minimum, refuse it in translation with a message that says what to write.

Tests: the rows are registered in test/oracles/candidates.json against this issue (family an empty captured collection cannot be a VALUES query root). Each fails as candidate fixed once the fix lands.

Found by CapturedCollectionProbeTests (route B, #105).

Activity

  1. added this to the 10.0.0 milestone on Oct 17, 2026
  2. added
    bugSomething isn't working
    found-by/oracleFound by the generated-oracle machinery (claims × API surfaces)
    area/rangesRange unions, variants and the shape engines
    area/value-setsValue set families and wrapper arities
    package/efcoreAffects CodoMetis.ValueRanges.EFCore.PostgreSQL
    severity/lowCosmetic, message or documentation-level difference
    on Oct 17, 2026
  3. CaffeinatedCoder commented on Oct 18, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Fixed in #170 (fix/empty-captured-collection, stacked on #169, 10.0.0), placed before the #76 fix on purpose since that fix would otherwise turn an empty range list from a correct answer into this exception. What is now true:

    • EmptyCollectionRewriter, run from the decorated parameter-based SQL processor after the provider's expansion: a SELECT whose table list holds a VALUES with no rows gets one row of typed NULLs and FALSE on its WHERE, so it reads no rows — Any is false, Count is 0, All is true. The SQL is … FROM (VALUES (NULL::text[])) AS w("Value") WHERE FALSE. Only a VALUES whose referenced column is a registered range or value set type is rewritten.
    • Held by SetCollectionTranslationTests, RangeCollectionTranslationTests, SetCollectionContainsTests, RangeCollectionContainsTests and CapturedCollectionProbeTests (its Any/Count over an empty captured collection of value sets (or of ranges beside Contains) throws 'Empty collections are not supported as inline query roots' #137 candidate removed); with the rewriter removed they fail with EF Core's message and the probe reports 176 lines. Changelog 10.0.0 Fixed row and docs/efcore.md bullet added.
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/rangesRange unions, variants and the shape enginesarea/value-setsValue set families and wrapper aritiesbugSomething isn't workingfound-by/oracleFound by the generated-oracle machinery (claims × API surfaces)package/efcoreAffects CodoMetis.ValueRanges.EFCore.PostgreSQLseverity/lowCosmetic, message or documentation-level difference

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions