Fix decorator parameter selection for derived-service dependencies (#1459)#1484
Merged
Conversation
…1459) The compatibleServiceParameter added for #1330 matched any decorator constructor parameter whose type was assignable-from the service type (serviceType.IsAssignableFrom(pi.ParameterType)). When a decorator took a dependency typed as a more-derived service (e.g. a sub-interface of the decorated service), that parameter matched the predicate and received the decorated instance even though the instance was not actually of the more derived type, throwing InvalidCastException. Additionally require pi.ParameterType.IsInstanceOfType(currentInstance) so the decorated instance is only injected where it is genuinely assignable; otherwise the parameter falls through to normal autowiring. This narrows the match set, preserving #1330 behavior.
Mirrors the closed-generic DecoratorTests case: an open-generic decorator whose constructor takes a more-derived service dependency (IDecoratedService<T>) than the decorated service (IService<T>) must resolve that dependency from the container rather than receiving the decorated instance. Fails with InvalidCastException against the pre-fix middleware.
- Convert the closed-generic DerivedDependencyDecorator from a record to a plain class for consistency with the other decorator types in the file. - Add a decorator-chain regression test exercising the DecoratorContext UpdateContext path: the outer decorator's more-derived dependency must still resolve from the container when the chained instance (inner decorator output) is not assignable to it. Fails with InvalidCastException against the pre-fix middleware. - Add a test asserting a clean DependencyResolutionException when the more-derived dependency is unregistered (rather than InvalidCastException). - Make the open-generic test self-contained with a purpose-built IDerivedService<T> sub-interface instead of reusing shared test infra.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #1484 +/- ##
========================================
Coverage 77.77% 77.78%
========================================
Files 217 217
Lines 5827 5829 +2
Branches 1252 1253 +1
========================================
+ Hits 4532 4534 +2
Misses 759 759
Partials 536 536 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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 #1459. When a decorator's constructor takes a dependency typed as a more-derived service than the one being decorated (e.g. a sub-interface), Autofac would force-inject the decorated instance into that parameter even though the instance wasn't actually of that derived type — throwing
InvalidCastException.Root cause
The
compatibleServiceParameterinDecoratorMiddleware.Execute(added for #1330) matched any decorator constructor parameter whose type was assignable-from the service type:In the #1459 repro,
D(IAb ab, IA a)decoratesIA. SinceIA.IsAssignableFrom(IAb)is true, the decoratedAinstance (only anIA, not anIAb) was injected into theIAbparameter, crashing.Fix
Narrow the predicate so it only supplies the decorated instance to parameters the instance can actually be assigned to:
This is a strict narrowing of the match set: the #1330 behavior is preserved (where the decorated instance genuinely is the more-derived type), while parameters the instance can't satisfy fall through to normal autowiring (resolving the correct registration). Previously these cases only ever threw
InvalidCastException, so no working behavior changes.Tests
DecoratorContext.UpdateContextpath (chained instance not assignable to the outer decorator's derived dependency).DependencyResolutionExceptionrather than a crash.All decorator tests pass (76); full
Autofac.Testsuite passes (836). Each regression test was confirmed to fail against the pre-fix middleware.Versioning
Bumped
default.projto 9.1.1 (PATCH) — a backwards-compatible bug fix; no public API change.Note
This branch also carries an unrelated
.pre-commit-config.yamlpin update (markdownlint) committed during the work to unblock local hooks.