Repository navigation
Honor [FromKeyedServices] on constructor parameters in generated code - #445
Merged
Merged
Conversation
ConstructorFrame.FindVariables resolved each constructor parameter via chain.FindVariable(parameter.ParameterType) — the type-only overload — which discards the ParameterInfo and any attributes on it. As a result the [FromKeyedServices] attribute on a constructor parameter was ignored during code generation: the parameter fell through to the default registration, so two keyed parameters of the same service type both resolved to whichever registration was last (the family default). This only affected constructor injection. Method parameters already worked because MethodCall.FindVariables resolves via the attribute-aware chain.FindVariable(ParameterInfo) overload, which MethodFrameArranger uses to detect [FromKeyedServices] and resolve the keyed service. ConstructorFrame now uses the same overload. Adds keyed_constructor_injection covering a constructor with two [FromKeyedServices] parameters of the same service type: it asserts the generated code resolves each key to its own implementation, and compiles + runs the result to prove each parameter lands the correct keyed instance in the correct slot. Reported in JasperFx/wolverine#3081.
jeremydmiller
added a commit
to JasperFx/wolverine
that referenced
this pull request
Jun 12, 2026
…loses #3081) (#3086) JasperFx 2.9.10 fixes ConstructorFrame to resolve constructor parameters via the attribute-aware FindVariable(ParameterInfo) overload, so [FromKeyedServices] on a handler's constructor is honored (JasperFx/jasperfx#445). Before, keyed constructor parameters were resolved by type only and fell through to the default (last) registration — two keyed params of the same service type both bound to the same instance. - Bump JasperFx, JasperFx.Events, JasperFx.Events.SourceGenerator and JasperFx.SourceGenerator 2.8.2 -> 2.9.10 (RuntimeCompiler stays on its 5.x line). - Add keyed_services_on_handler_constructor: a handler whose constructor takes two [FromKeyedServices] params of the same service type, asserting each resolves to its own registration. Companion to using_with_keyed_services, which covers keyed services on Handle METHOD parameters (that path already worked). - Bump MessagePack 3.1.3 -> 3.1.7 to clear the NU1903 advisory that otherwise fails the build under TreatWarningsAsErrors. Closes #3081 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Fixes the root cause of JasperFx/wolverine#3081: Wolverine handler code generation ignored
[FromKeyedServices]on a handler's constructor parameters.Root cause
ConstructorFrame.FindVariablesresolved each constructor parameter by type only:That discards the
ParameterInfoand the attributes on it, so the[FromKeyedServices("…")]key was dropped. The parameter fell through to the default registration — meaning two keyed parameters of the same service type both resolved to whichever registration was last (the family default).This only affected constructor injection. Method parameters already worked, because
MethodCall.FindVariablesresolves via the attribute-awarechain.FindVariable(ParameterInfo)overload thatMethodFrameArrangeruses to detect[FromKeyedServices]and resolve the keyed service. That asymmetry is exactly why keyed services on aHandle(...)method parameter worked in Wolverine but the same attribute on the handler's constructor silently resolved to the wrong instance.Fix
One line —
ConstructorFramenow uses the sameParameterInfooverload asMethodCall:Non-keyed parameters are unaffected:
MethodFrameArranger.FindVariable(ParameterInfo)falls through to the type-based resolution when there is no attribute (and name-matches a same-typed variable, consistent with how method parameters already resolve).Test
keyed_constructor_injection(inCodegenTests/Services) mirrors the existingkeyed_service_locationharness but exercises the constructor path viaServiceContainer.TryCreateConstructorFrames— the exact path Wolverine uses to build a handler instance. A class with two[FromKeyedServices]constructor parameters of the same service type:each_keyed_constructor_parameter_resolves_to_its_own_registration— the generated code references both implementations (before the fix, only the last/default appeared);keyed_constructor_parameters_resolve_correctly_at_runtime— compiles and runs the generated harness and asserts each parameter lands the correct keyed instance in the correct slot.Both fail without the fix. Full
CodegenTestsServices/codegen/frames suite (406 tests) is green; the existingkeyed_service_locationtests still pass.Follow-up
Once this is published, a follow-up PR to Wolverine will bump the JasperFx dependency and add an end-to-end reproduction test for #3081 (keyed constructor injection through a Wolverine handler, including under Lamar — whose keyed resolution is already correct).
🤖 Generated with Claude Code