Closed schwarzr closed 1 year ago
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.
area-Extensions-DependencyInjection
Tagging subscribers to this area: @dotnet/area-extensions-dependencyinjection See info in area-owners.md if you want to be subscribed.
Author: | schwarzr |
---|---|
Assignees: | - |
Labels: | `untriaged`, `area-Extensions-DependencyInjection` |
Milestone: | - |
I'd be interested in working on this.
It would be helpful to confirm my understanding of the precise desired behavior in cases like this, though. Trying to piece together the intent from the code:
Is that all correct?
I believe it's the same issue as this https://github.com/dotnet/runtime/pull/68053. We should confirm though, it only happens when the environment is development right?
I believe it's the same issue as this https://github.com/dotnet/runtime/pull/68053
@davidfowl that seems right; potentially the fix there did not fully resolve the problem.
I believe it's the same issue as this https://github.com/dotnet/runtime/pull/68053. We should confirm though, it only happens when the environment is development right?
@schwarzr should confirm, but from my repro the code shown requires development environment because in that setup it is being triggered by ValidateOnBuild = true
in the ServiceProviderOptions
.
However, ValidateOnBuild
is not the only way to trigger this. For example the following also repros for me:
void Main()
{
var sc = new ServiceCollection();
sc.AddTransient<IProcessor<string>, SampleProcessor>();
sc.AddTransient(typeof(IProcessor<>), typeof(GenericProcessor<>));
// not using build validation
using var s = sc.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = false });
s.GetService(typeof(IProcessor<string>)); // resolve this first: gives SampleProcessor
s.GetServices<IProcessor<string>>(); // now this gives [SampleProcessor, SampleProcessor]
}
public interface IProcessor<T> { }
public class SampleProcessor : IProcessor<string> { }
public class GenericProcessor<T> : IProcessor<T> { }
However, ValidateOnBuild is not the only way to trigger this. For example the following also repros for me:
That raises the priority of this. This was assumed to only be broken in validation scenarios.
@madelson
@schwarzr should confirm, but from my repro the code shown requires development environment because in that setup it is being triggered by
ValidateOnBuild = true
in theServiceProviderOptions
.
I have been able to reproduce this issue outside Visual Studio and without ValidateOnBuild = true. So no, i don't think it is limited do the development environment.
@davidfowl One thing i recognized when i investigated this issue is, that if you register the generic version twice the resulting instances are: [SampleProcessor, GenericProcessor, SampleProcessor]
void Main()
{
var sc = new ServiceCollection();
sc.AddTransient<IProcessor<string>, SampleProcessor>();
sc.AddTransient(typeof(IProcessor<>), typeof(GenericProcessor<>));
sc.AddTransient(typeof(IProcessor<>), typeof(GenericProcessor<>));
// not using build validation
using var s = sc.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = false });
s.GetService(typeof(IProcessor<string>)); // resolve this first: gives SampleProcessor
s.GetServices<IProcessor<string>>(); // now this gives [SampleProcessor, GenericProcessor, SampleProcessor]
}
public interface IProcessor<T> { }
public class SampleProcessor : IProcessor<string> { }
public class GenericProcessor<T> : IProcessor<T> { }
and if you register the generic service first, it kind of works as expected.
void Main()
{
var sc = new ServiceCollection();
sc.AddTransient(typeof(IProcessor<>), typeof(GenericProcessor<>));
sc.AddTransient<IProcessor<string>, SampleProcessor>();
// not using build validation
using var s = sc.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = false });
s.GetService(typeof(IProcessor<string>)); // resolve this first: gives SampleProcessor
s.GetServices<IProcessor<string>>(); // now this gives [GenericProcessor, SampleProcessor]
}
public interface IProcessor<T> { }
public class SampleProcessor : IProcessor<string> { }
public class GenericProcessor<T> : IProcessor<T> { }
Are there any requests to backport the fix to v7 and possible v6 (since it's LTS)?
Description
When registring a service in the IServiceCollection of asp.net core >= 6.0 once with a concrete type and once as a generic registration you get the wrong types of instances from the ServiceProvider.
Reproduction Steps
Create a new asp.net core 6 Project:
Program.cs
IProcessor
GenericProcessor
SampleEntity
SampleEntityProcessor
Expected behavior
should return
Actual behavior
currently returns
Regression?
Tested with:
Known Workarounds
No response
Configuration
Windows 11 x64 .net runtime 6.0.12 .net runtime 7.0.1 Visual Studio 2022
Other information
No response