Skip to content

Per-domain range set types (Int32RangeSet, DateRangeSet, …) replace RangeSet<TRange, T> #275

Open

Description

@CaffeinatedCoder

Problem

RangeSet<TRange, T> makes every consumer spell both type arguments — RangeSet<DateRange, DateOnly> — although T is fully determined by TRange. C# does not infer type arguments from constraints, so the element type has to be named at every declaration: entity properties, DTOs, method signatures. The From remarks and docs/ranges.md already tell consumers to prefer the collection expression because of it.

The quirk is not local to one declaration. Everything around the set exists because an open generic cannot name a closed type:

  • RangeSet names RangeJsonConverterFactory and registers closed converters through GenericConverters.Register, because an attribute cannot name a converter closed over the set's own type (AGENTS.md, "AOT").
  • The EF translators match members by typeof(RangeSet<,>) and nameof(RangeSet<,>.Union) in ValueRangesMethodCallTranslator, ValueRangesQueryExpressionInterceptor and ValueRangesAggregateMethodCallTranslator, instead of a registry lookup by CLR type as the ranges do.
  • RangeAgg / RangeIntersectAgg over ranges are thirteen per-type overloads for the same reason (docs/architecture.md, "Aggregates").
  • YearMonthRangeSetTypeMapping is a hand-written special case beside the generic ValueRangeSetTypeMapping<TRange, T>.

Change

One sealed set type per domain, built as the value-set family is (StringSet, GuidSet, …):

  • Core: Int32RangeSet, Int64RangeSet, DecimalRangeSet, DateRangeSet, DateTimeRangeSet, DateTimeOffsetRangeSet, TimeRangeSet, Float64RangeSet, Float32RangeSet. NodaTime satellite: LocalDateRangeSet, LocalDateTimeRangeSet, InstantRangeSet, YearMonthRangeSet. The RangeSet suffix keeps the vocabulary the docs, the claim catalogue and area/range-set use; Int32Set (values) against Int32RangeSet (ranges) reads correctly.
  • An IRangeSet<TRange, T> interface and a static-abstract IRangeSetFactory<TSet, TRange, T>, mirroring IValueSet<T> / IValueSetFactory<TSet, T>. The current body of RangeSet.cs moves into an internal engine (Internals/) the thirteen types delegate to, so the algebra stays in one place and a fix to set semantics is one change.
  • Generic producers (Union, Except, Gaps, ToRangeSet, the set aggregates, RangeTypeDefinition<TRange, T>) learn the set type from the range side: a third type parameter on IRangeFactory<TRange, T> or a companion interface with static abstract TSet CreateSet(ReadOnlySpan<TRange>). The infrastructure generics then carry three parameters a consumer never writes. Per-type overloads (the RangeAgg precedent) are the fallback, not the plan.
  • Each set type names its own closed [JsonConverter] and [TypeConverter]; the GenericConverters registration for sets and the factory indirection go. The EF side looks sets up through RangeTypeRegistry by CLR type (RangeSetClrType already exists) and drops the typeof(RangeSet<,>) matches. YearMonthRangeSet stops being the odd one out.
  • RangeSet<TRange, T> and the non-generic RangeSet builder are removed. Int32RangeSet set = [a, b]; keeps working through [CollectionBuilder] on each type.
  • New clone-sweep family (core range sets, nodatime range sets) in clone-waivers.json; claims in claims.json re-pointed; RangeProbes, RangeSetLiftParityTests and the EF discovery pick the types up by reflection.
  • docs/migration.md v10.x section gives the rename as a regex (RangeSet<(\w+)Range, \w+> → $1RangeSet), and the changelog's "what can break you" list carries it.

Why 11.0.0 and not 10.0.0

10.0.0 tags on the commit the gate (#179) passed on 2026-10-19; its changelog opens with "Nothing was removed or renamed", and additions were frozen on 2026-10-18. Epic #147 reserves surface changes for the 11 line, and since #149 (closed unions) turned out non-breaking, this is the one breaking surface change 11.0.0 carries. The cost is that the two lines differ in the set family for the LTS lifetime, so a set-semantics fix on main is a manual port into release/10.x; keeping the algebra in a shared engine keeps that port small. 10.x consumers can alias (using DateRangeSet = CodoMetis.ValueRanges.RangeSet<CodoMetis.ValueRanges.DateRange, System.DateOnly>;) in the meantime; docs/ranges.md should say so.

Footprint (measured 2026-10-19 on main)

Area Mentions of RangeSet<
Shipping source outside RangeSet.cs about 90 across core, EF and both satellites
Test files 87 files, 397 mentions
claims.json 180 lines mention RangeSet

Verification

  • Discovered tests and the clone sweep list what is missing for each new sibling (docs/architecture.md#adding-a-range-or-set-type); dotnet run tools/oracles.cs clones clean or waived.
  • JsonDefaultContractTests, TypeConverterContractTests, OracleSurfaceCoverageTests and OracleCatalogueTests pass with the thirteen types and no RangeSet<,>.
  • Every EF translation test asserts the same SQL as before; ShapeMatrixParityTests, RangeSetLiftParityTests, TemporalStoreParityTests and the aggregate integration tests pass on PostgreSQL 16 and 18.
  • The Native AOT consumer in test/consumer-smoke-test.sh declares and serializes at least one set type per package.
  • ApiCompat suppression file lists every removed member; each is in the changelog's "what can break you" list.

Sequence on the 11 line

After 11.0.0-preview.1 (#103, pure retarget) and preview.2 (#149, closed unions): this is the third step before 11.0.0.

Found-By: maintainer, API review of the 10.0.0 surface

Activity

  1. added this to the 11.0.0 milestone on Oct 19, 2026
  2. added
    enhancementNew feature or request
    breakingChanges an answer or a signature — needs a major
    area/range-setRangeSet and multirange algebra
    package/nodatimeAffects CodoMetis.ValueRanges.NodaTime
    package/efcoreAffects CodoMetis.ValueRanges.EFCore.PostgreSQL
    package/coreAffects CodoMetis.ValueRanges
    package/efcore-nodatimeAffects CodoMetis.ValueRanges.EFCore.PostgreSQL.NodaTime
    on Oct 19, 2026
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 algebrabreakingChanges an answer or a signature — needs a majorenhancementNew feature or requestpackage/coreAffects CodoMetis.ValueRangespackage/efcoreAffects CodoMetis.ValueRanges.EFCore.PostgreSQLpackage/efcore-nodatimeAffects CodoMetis.ValueRanges.EFCore.PostgreSQL.NodaTimepackage/nodatimeAffects CodoMetis.ValueRanges.NodaTime

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions