Only subscribe Module registry events when hooks are overridden (#1446)#1487
Merged
Conversation
…not overridden In Module.Configure, AttachToRegistrations and AttachToSources previously subscribed lambda event handlers to componentRegistry.Registered and componentRegistry.RegistrationSourceAdded unconditionally — even when the concrete module subclass overrode neither AttachToComponentRegistration nor AttachToRegistrationSource. With many modules this caused O(N^2) delegate invocations and excessive closure allocations at container build time. The fix uses reflection to check whether the virtual method is overridden on the concrete module type (DeclaringType != typeof(Module)), and only subscribes the event handler when an override is present. The check handles overrides on intermediate base classes correctly. Results are cached per-type in the existing InternalReflectionCaches infrastructure (Usage = Registration) to avoid repeated reflection on subsequent Configure calls for the same type.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #1487 +/- ##
===========================================
+ Coverage 77.69% 77.81% +0.11%
===========================================
Files 217 217
Lines 5829 5874 +45
Branches 1253 1258 +5
===========================================
+ Hits 4529 4571 +42
- Misses 764 765 +1
- Partials 536 538 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
- Remove AAA comments from module tests; reference #1446 in each - Align test names with the file's existing convention - Add independence guards: overriding one hook must not subscribe to the other event - Add ModuleRegistrationBenchmark measuring container build cost vs module count
This was referenced Jun 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #1446.
Module.Configureunconditionally subscribed lambda event handlers tocomponentRegistry.RegisteredandcomponentRegistry.RegistrationSourceAdded, even when the concrete module overrode neitherAttachToComponentRegistrationnorAttachToRegistrationSource. Each subscription replays every existing registration, so with many registered modules this produced O(N²) delegate invocations and allocations at build time (the reporter measured ~1.56 GB allocated and ~22× slower build vs. ~55 MB without the subscriptions).Fix
The two private subscription helpers now subscribe only when the derived module type actually overrides the corresponding virtual method, detected via reflection (
GetMethod(...).DeclaringType != typeof(Module), using exact parameter-type arrays to select the right overload). This correctly handles overrides declared in an intermediate base class. Results are cached per-type inInternalReflectionCaches(registration-scoped, cleared after build) to avoid repeated reflection.Modules that override the hooks behave identically to before; only modules that override neither stop subscribing — which is the entire point.
Tests
Tests reference #1446 and follow the repo conventions (no AAA comments; names aligned with the existing file style). Coverage:
AttachToComponentRegistration(orAttachToRegistrationSource) still subscribes and still has its hook invoked (regression guards).Each "does not subscribe" test was confirmed to fail when the fix is disabled (forced always-subscribe), so they are genuine guards rather than tautologies. One pre-existing test (
ModifiedScopesHaveTheirOwnDelegate) was updated to useTryGetValuefor a registry property key that is now legitimately absent when no hook is overridden.Benchmark
Adds
ModuleRegistrationBenchmark(bench/Autofac.Benchmarks/, registered inBenchmarkSet.All) measuring container build cost vs. module count, with theMemoryDiagnoserfrom the existing config. Run it against a pre-fix package to see the difference:Measured results (1000 no-hook modules):
Build time also scales roughly linearly with module count after the fix instead of quadratically.
No public API changes. Zero warnings/errors; full suites pass on net8.0 and net10.0.