Skip to content

Only close open generic registrations once per closed service type - #617

Merged
seesharper merged 1 commit into
masterfrom
fix-duplicate-closed-generic-enumerables
Sep 25, 2026
Merged

seesharper merged 1 commit into
masterfrom
fix-duplicate-closed-generic-enumerables

Conversation

@seesharper

Copy link
Copy Markdown
Owner

Follow-up to #616 (found while investigating #614).

Problem

With EnableMicrosoftCompatibility, CreateEmitMethodForEnumerableServiceServiceRequest closes all open generic registrations and appends them to allEmitters every time an enumerable emit method is created for a closed generic service type. Emit methods are created more than once for the same type (as a dependency, as a root service, via CanGetInstance), so items accumulate:

container.Register(typeof(IBehavior<>), typeof(FirstBehavior<>));
container.Register(typeof(IBehavior<>), typeof(SecondBehavior<>));

container.GetInstance<Handler<int>>();                 // IEnumerable<IBehavior<int>> -> 2 items
container.GetInstance<IEnumerable<IBehavior<int>>>();  // 4 items (expected 2)

Calling CanGetInstance first gives 4 and 6. A closed generic that was first registered through a single-service request (CreateEmitMethodBasedOnClosedGenericServiceRequest) was also added again by the enumerable path. For MediatR users this means pipeline behaviors can run multiple times.

Without Microsoft compatibility the problem was masked because emitters are stored per service name (TryAdd).

Fix

Track, per closed generic service type, which open generic registrations (identified by their unique RegistrationOrder) have already been closed, and skip them on subsequent requests. The single-service path marks its registration too, when the closed registration uses the same service name as the open one.

Tests

  • ClosedGenericEnumerableTests covers resolving the enumerable multiple times, after CanGetInstance, and after a single-service resolve, with and without Microsoft compatibility. The MS-compat cases fail on master and pass with the fix.
  • Full suite passes (1567 tests).
  • LightInject.Microsoft.DependencyInjection's test suite (including the MS.DI specification tests) passes against this build (266 tests).

🤖 Generated with Claude Code

With Microsoft compatibility enabled, every enumerable request for the same
closed generic service type closed the open generic registrations again and
appended them to the list of emitters, resulting in duplicate items (e.g.
IEnumerable<IBehavior<int>> returning 4 items instead of 2). The same
happened when a closed generic had already been registered through a
single service request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@seesharper
seesharper merged commit 56bdbf2 into master Sep 25, 2026
2 checks passed
@seesharper seesharper mentioned this pull request Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant