Skip to content

A 5.4.1 satellite with core 5.5.0 is refused as the core differ; satellites accept any later core while binding to its internals #169

Closed

Description

@CaffeinatedCoder

Found by a pre-release sweep of main @ eff80b4 (lens: delivery); reproduced with packed packages in a consumer outside the repository. Should be decided before the tag: the satellites' dependency range on core goes into the published 5.5.0 nuspecs and cannot change afterwards.

Problem

CustomMigrationsModelDiffer.ValidateNothingOnlyTheSatelliteRenders (#150) skips its refusals when IsSatelliteDiffer is true (CustomMigrationsModelDiffer.cs:1030, :1052). That member is internal virtual, false in core, and overridden only by the 5.5.0 satellites. A 5.4.1 satellite assembly was compiled before the member existed, so on core 5.5.0 its differ counts as the core differ and is refused.

NuGet allows this pairing. The 5.4.1 satellite nuspecs declare EFCore.ComplexIndexes 5.4.1, which means >= 5.4.1, so a consumer who references both packages and merges only the Dependabot PR for core gets satellite 5.4.1 with core 5.5.0. The restore and the build report no warning.

The 5.5.0 satellites still declare >= 5.5.0 on core while binding to its internals through InternalsVisibleTo. A later core release can therefore break a 5.5.0 satellite the same way, on a line that takes fixes until 2028.

Scenario

A consumer references EFCore.ComplexIndexes.PostgreSQL 5.4.1 and EFCore.ComplexIndexes 5.5.0 directly. They call UseNpgsqlComplexIndexes() and declare HasExclusionConstraint(x => x.Ship.City, x => x.Period, name: "ex_city_period").

  • deps.json resolves EFCore.ComplexIndexes 5.5.0 next to EFCore.ComplexIndexes.PostgreSQL 5.4.1.
  • GenerateCreateScript() throws. EnsureCreated(), Migrate() and migrations add throw the same way (the last three traced through the same check):

InvalidOperationException: 'Order' declares a PostgreSQL exclusion constraint ('ex_city_period'), but the model is diffed by the provider-agnostic core differ … call UseNpgsqlComplexIndexes() instead of UseComplexIndexes()

The consumer already calls UseNpgsqlComplexIndexes(). With both packages at 5.5.0, the same program renders EXCLUDE. The failure is loud, not silent, but its advice is wrong. By the same trace, not run: SQL Server 5.4.1 with core 5.5.0 is refused for a unique index over nullable columns.

Change

  • In core, decide "satellite differ" without an override that older assemblies lack. Use the rule ComplexIndexDesignTimeRegistration.IsSatelliteDiffer already applies: a type derived from CustomMigrationsModelDiffer in another assembly. This is the only fix that reaches the published 5.4.x satellites.
  • Pin each satellite's dependency on core to [$(Version)], since the satellites bind to core internals. Then NuGet refuses a mixed pair at restore instead of running it.

Test

A PackagingConventionTests assertion that each satellite's nuspec declares core as [x.y.z]. A core unit test that a CustomMigrationsModelDiffer subclass from another assembly, without the override, is not refused.

Activity

  1. added this to the 5.5.0 milestone on Oct 20, 2026
  2. added
    bugSomething isn't working
    severity/mediumInconsistency or failure in a narrow scenario
    package/coreAffects EFCore.ComplexIndexes
    package/postgresqlAffects EFCore.ComplexIndexes.PostgreSQL
    on Oct 20, 2026
  3. CaffeinatedCoder commented on Oct 20, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Fixed in #174, with a different runtime change from the one proposed above.

    • Range: the satellites depend on the core at exactly [$(Version)]. The three packages always ship together, so an exact pin never refuses a released pair. A range up to the next minor would accept core 5.5.1 under satellite 5.5.0, which is the pair to keep out.
    • Measured with packed packages: a direct core reference above the pin restores with only NU1608 (a warning), and one below it fails with NU1605. The published 5.4.x satellites declare >=, so the pin doesn't reach them.
    • Runtime: the core differ refuses a satellite assembly of another version and names both versions. It doesn't decide "satellite" by type, because that would run a 5.4.1 satellite against 5.5.0 internals, a pair never built or tested. It would also exempt a provider's own subclass of the core differ from UseComplexIndexes() on PostgreSQL or SQL Server still drops exclusion and temporal constraints and the IS NOT NULL filter without a word #150's refusals.

    The test for that last point checks that a subclass in an assembly that isn't a satellite is left alone.

  4. added a commit that references this issue on Oct 20, 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/releaseVersioning, changelogs, release wiringbugSomething isn't workingpackage/coreAffects EFCore.ComplexIndexespackage/postgresqlAffects EFCore.ComplexIndexes.PostgreSQLpackage/sqlserverAffects EFCore.ComplexIndexes.SqlServerseverity/mediumInconsistency or failure in a narrow scenario

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions