Skip to content

Keyedinject directive#12761

Open
wlsquid wants to merge 14 commits intodotnet:mainfrom
wlsquid:keyedinject_directive
Open

Keyedinject directive#12761
wlsquid wants to merge 14 commits intodotnet:mainfrom
wlsquid:keyedinject_directive

Conversation

@wlsquid
Copy link

@wlsquid wlsquid commented Feb 11, 2026

Summary

This PR introduces a new Razor directive, @keyedinject, to support keyed dependency injection in Razor views and components.
The directive is intentionally defined with a fixed token shape where the key is a required string literal placed at the end of the directive.

@keyedinject MyApp MyPropertyName "KeyOne"

This approach is backward compatible and avoids changes to Razor’s parsing or directive grammar.

Overview

I initially made this out of curiosity after reviewing #9286. For backwards compatibility, avoiding parser changes, keeping with existing conventions, simplicity and not opening up bigger discussions I have gone with an approach of making a new directive for keyed DI injections and having the key as a non-optional string token on the end of the directive (as the key is kind of like a dictionary key).

This was for the following reasons:

  1. Directives do not support optional preceding tokens. Adding them in is not a trivial task and would require significant changes to how razor/cshtml is parsed. Supporting optional preceding tokens would require non-trivial changes to the Razor parser, directives etc. which is outside the scope of this change.

  2. Semicolons. The inject attribute supports semi-colons. From what I could see unless I did something very hacky or re-wrote how razor/cshtml parsed then having an optional string token means something could get scooped up as the third attribute. Doing either of those things is a bit unnecessary and beyond the scope of the issue.

  3. The key is intentionally placed last along with using a separate @keyedinject directive identifier to avoid ambiguous parsing and malformed directive cascades when the key is omitted.

  4. Keyed injection has conceptual distinction from regular injection.
    A keyed service must always be resolved using a key. Representing this via a separate directive makes the behavior explicit.

  5. The other proposals were not aligned with existing directive patterns. They were really just member declarations in razor.
    A preceding key was also suggested.

    In addition to the syntax/parsing issues mentioned above, this seemed akin to accessing a dictionary by writing [“someKey”]someDictionary or passing an argument with a value in a CLI by ssh 2222 -p. To me it seems you go in almost every case <Type of Thing> then <Selector>. This may be better served by a @member/@classmember directive which seems beyond the scope of the issue/feature.

    The <Type> <MemberName> <Key> syntax mirrors existing directive usage and avoids introducing member-declaration-style syntax, which is not idiomatic for Razor directives.

Risks

This adds the feature without breaking backward compatibility or opening up unnecessary complexity. A possible downside is if more directive syntax options are introduced later, but the code should be easy to consolidate if that happens.

Non feature related changes

  • No existing directives are modified.
  • No changes were made to Razor’s parser or grammar.
  • All existing and new tests pass, and source mapping baselines have been updated accordingly.
  • However, I am not certain I have not left off any moving parts.

end ✌

This is my first pull request to this repo so I hope this is not a bother - feedback is welcome. I tried my best to stay within the coding style/guidelines that I found in the code base and to make the least disruptive changes. I hope it is welcome.

@wlsquid wlsquid requested a review from a team as a code owner February 11, 2026 13:34
@wlsquid
Copy link
Author

wlsquid commented Feb 11, 2026

@dotnet-policy-service agree

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