Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Keyed Services Support to Dependency Injection #87183

Merged
merged 43 commits into from
Jul 13, 2023

Conversation

benjaminpetit
Copy link
Member

Related issue: #64427

Since my last proposal (https://gist.github.com/benjaminpetit/49a6b01692d0089b1d0d14558017efbc) we made some changes and took some decisions.

This PR is still in draft, but everything is working and can be reviewed. Naming is not definitive, and some exception needs to be replaced.

Changes since the proposal:

  • introduced ServiceKeyAttribute: used to inject the key used to resolve the service in the constructor (see test ResolveKeyedServiceSingletonInstanceWithKeyInjection
  • introduced FromKeyedServices: used to get a keyed service from DI in the constructor (see test ResolveKeyedServiceSingletonInstanceWithKeyedParameter)
  • introduced the notion of "any key registration": the user can register a "catchall" key that will resolve any key. Useful to injet logger with custom category, instead of relying on open generics for example.

@dotnet-issue-labeler
Copy link

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@ghost ghost assigned benjaminpetit Jun 6, 2023
@ghost
Copy link

ghost commented Jun 6, 2023

Tagging subscribers to this area: @dotnet/area-extensions-dependencyinjection
See info in area-owners.md if you want to be subscribed.

Issue Details

Related issue: #64427

Since my last proposal (https://gist.github.com/benjaminpetit/49a6b01692d0089b1d0d14558017efbc) we made some changes and took some decisions.

This PR is still in draft, but everything is working and can be reviewed. Naming is not definitive, and some exception needs to be replaced.

Changes since the proposal:

  • introduced ServiceKeyAttribute: used to inject the key used to resolve the service in the constructor (see test ResolveKeyedServiceSingletonInstanceWithKeyInjection
  • introduced FromKeyedServices: used to get a keyed service from DI in the constructor (see test ResolveKeyedServiceSingletonInstanceWithKeyedParameter)
  • introduced the notion of "any key registration": the user can register a "catchall" key that will resolve any key. Useful to injet logger with custom category, instead of relying on open generics for example.
Author: benjaminpetit
Assignees: benjaminpetit
Labels:

new-api-needs-documentation, area-Extensions-DependencyInjection

Milestone: -

@benjaminpetit benjaminpetit marked this pull request as ready for review June 9, 2023 13:33
@benjaminpetit benjaminpetit added the api-ready-for-review API is ready for review, it is NOT ready for implementation label Jun 9, 2023
@tillig
Copy link
Contributor

tillig commented Jun 14, 2023

Shouldn't there be some specification tests to ensure containers that implement this are doing it right? The spec tests also indicate how the .NET team intends to use the feature (to "document" both explicit and implicit behavior requirements) so having that would be helpful.

Copy link
Member

@halter73 halter73 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, I really like the approach. I'm a big fan of wrapping the key and type in ServiceIdentifier. I do think we should use string instead of object for the name. I also think we should do more to fail loudly when used with incompatible containers.

Stuff like IServiceProviderIsService support can probably come later. We should definitely fix the _realizedServices caching bug though.


public Service() => _id = Guid.NewGuid().ToString();

public Service([ServiceKey] string id) => _id = id;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be invalid for a [ServiceKey] parameter to be a string and not an object? People will want it to alwas be a string so they can store it in a string field like you do here. I think this proves my earlier point about how it's more convenient to work with this parameter when you know it will be a string.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree, people will not always want to use a string. And here it just works fine, not sure why we should force it to be an object, that's the responsibility of the developer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if someone tries to register this service using the AnyKey? Does it validate when we add the service or build the ServiceProvider? Or does it just throw an InvalidCastException when you try to resolve it?

I think we should probably at least be validating the runtime service key type is assignable to the [ServiceKey] parameter type in CallSiteFactory. This validation would make sense even if the service type had to be string.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will throw InvalidCastException, that's correct. I guess we could validate the parameter type.

@benjaminpetit
Copy link
Member Author

Shouldn't there be some specification tests to ensure containers that implement this are doing it right? The spec tests also indicate how the .NET team intends to use the feature (to "document" both explicit and implicit behavior requirements) so having that would be helpful.

I moved tests to the spec tests, I think they were all relevant.

namespace Microsoft.Extensions.DependencyInjection
{
[AttributeUsage(AttributeTargets.Parameter)]
public class FromKeyedServicesAttribute : Attribute
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sealed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, the developer can inherit from this attribute to use custom types for the key

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that a good idea though? Could that interfere with future source generators? We could always shoot of a quick email to get API approval for sealing it. It seems safer since we could always unseal later.

@benjaminpetit benjaminpetit added the blocking Marks issues that we want to fast track in order to unblock other important work label Jun 23, 2023
@benjaminpetit benjaminpetit removed the api-ready-for-review API is ready for review, it is NOT ready for implementation label Jun 29, 2023
@steveharter steveharter requested a review from buyaa-n July 7, 2023 20:32

namespace Microsoft.Extensions.DependencyInjection
{
public interface IServiceProviderIsKeyedService : IServiceProviderIsService
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making the API changes I suggested. As much as I like them, we should at least send an API review email to make sure any API adjustments are approved before merging. Or better yet discuss them in tomorrow's meeting.

Copy link
Member

@steveharter steveharter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM pending some minor API approvals.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Extensions-DependencyInjection blocking Marks issues that we want to fast track in order to unblock other important work new-api-needs-documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants