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

StartupValidator multiple registrations. #102061

Closed
PetSerAl opened this issue May 9, 2024 · 7 comments · Fixed by #102235
Closed

StartupValidator multiple registrations. #102061

PetSerAl opened this issue May 9, 2024 · 7 comments · Fixed by #102235

Comments

@PetSerAl
Copy link

PetSerAl commented May 9, 2024

OptionsBuilderExtensions.ValidateOnStart register StartupValidator service for option validation:

optionsBuilder.Services.AddTransient<IStartupValidator, StartupValidator>();

It does that with AddTransient, but ValidateOnStart can be called multiple times (once for each option type plus option name combination), which leads to StartupValidator would be registered multiple times. Should it use TryAddTransient instead to keep only one registration of StartupValidator?

@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label May 9, 2024
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label May 9, 2024
@vcsjones vcsjones added area-Extensions-Options and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels May 9, 2024
@tarekgh tarekgh added this to the Future milestone May 9, 2024
@tarekgh tarekgh removed the untriaged New issue has not been triaged by the area owner label May 9, 2024
@tarekgh
Copy link
Member

tarekgh commented May 9, 2024

@PetSerAl thanks for your report. Could you please submit a small sample code to demonstrate the problem you are seeing?

@tarekgh tarekgh added the needs-author-action An issue or pull request that requires more info or actions from the author. label May 9, 2024
@PetSerAl
Copy link
Author

using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

ServiceCollection sc = new();
sc.AddOptions<Option>("name1").Configure(o => o.Value = 1).Validate(o => o.Value > 0).ValidateOnStart();
sc.AddOptions<Option>("name2").Configure(o => o.Value = 2).Validate(o => o.Value > 0).ValidateOnStart();
sc.AddOptions<Option>("name3").Configure(o => o.Value = 3).Validate(o => o.Value > 0).ValidateOnStart();
sc.AddOptions<Option>("name4").Configure(o => o.Value = 4).Validate(o => o.Value > 0).ValidateOnStart();
sc.AddOptions<Option>("name5").Configure(o => o.Value = 5).Validate(o => o.Value > 0).ValidateOnStart();
sc.AddOptions<Option>("name6").Configure(o => o.Value = 6).Validate(o => o.Value > 0).ValidateOnStart();
sc.AddOptions<Option>("name7").Configure(o => o.Value = 7).Validate(o => o.Value > 0).ValidateOnStart();
sc.AddOptions<Option>("name8").Configure(o => o.Value = 8).Validate(o => o.Value > 0).ValidateOnStart();
sc.AddOptions<Option>("name9").Configure(o => o.Value = 9).Validate(o => o.Value > 0).ValidateOnStart();

foreach(var sd in sc.Where(sd => sd.ServiceType == typeof(IStartupValidator))) {
    Console.WriteLine($"{sd.ServiceType.FullName}|{sd.ImplementationType?.FullName}");
}

class Option {
    public int Value { get; set; }
}

Output:

Microsoft.Extensions.Options.IStartupValidator|Microsoft.Extensions.Options.StartupValidator
Microsoft.Extensions.Options.IStartupValidator|Microsoft.Extensions.Options.StartupValidator
Microsoft.Extensions.Options.IStartupValidator|Microsoft.Extensions.Options.StartupValidator
Microsoft.Extensions.Options.IStartupValidator|Microsoft.Extensions.Options.StartupValidator
Microsoft.Extensions.Options.IStartupValidator|Microsoft.Extensions.Options.StartupValidator
Microsoft.Extensions.Options.IStartupValidator|Microsoft.Extensions.Options.StartupValidator
Microsoft.Extensions.Options.IStartupValidator|Microsoft.Extensions.Options.StartupValidator
Microsoft.Extensions.Options.IStartupValidator|Microsoft.Extensions.Options.StartupValidator
Microsoft.Extensions.Options.IStartupValidator|Microsoft.Extensions.Options.StartupValidator

Same service type with the same implementation type registered multiple times.

@dotnet-policy-service dotnet-policy-service bot added needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration and removed needs-author-action An issue or pull request that requires more info or actions from the author. labels May 10, 2024
@tarekgh tarekgh removed the needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration label May 10, 2024
@tarekgh
Copy link
Member

tarekgh commented May 13, 2024

@PetSerAl I think you are correct. TryAddTransient should be used instead of AddTransient.

@eerhardt @davidfowl @steveharter @ericstj do you see any risk fixing this? to summarize the issue, every time OptionsBuilder<TOptions>.ValidateOnStart() get called, it does

optionsBuilder.Services.AddTransient<IStartupValidator, StartupValidator>();

which always add transit service to the DI using the same interface type IStartupValidator and implementation type StartupValidator. Using TryAddTransient should be used instead to avoid adding the same thing multiple times.

@ericstj
Copy link
Member

ericstj commented May 14, 2024

That seems correct, looks like that's what the old extensions library did: https://github.com/dotnet/extensions/blob/c9b1f395705c56438fb4f9eba351d6e0db624f12/src/ToBeMoved/Hosting.StartupInitialization/Internal/StartupInitializationBuilder.cs#L45C18-L45C33

@tarekgh
Copy link
Member

tarekgh commented May 14, 2024

@PetSerAl are you interested to submit a PR for the fix?

@PetSerAl
Copy link
Author

@PetSerAl are you interested to submit a PR for the fix?

Not any time soon. I currently do not have environment prepared for this.

@tarekgh
Copy link
Member

tarekgh commented May 14, 2024

@PetSerAl no worries, I'll do it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants