-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
GetServiceOrCreateInstance attempts to call wrong constructor when no types registered in D.I. #45119
Comments
Tagging subscribers to this area: @eerhardt, @maryamariyan Issue DetailsOriginal issue: dotnet/efcore#23306 Using these types: public class FailureContext : DbContext
{
public FailureContext(ILogger<FailureContext> logger)
{
Console.WriteLine($"With logger {logger}");
}
public FailureContext()
{
Console.WriteLine("Parameterless");
}
} This works for both var serviceProvider1 = new ServiceCollection()
.AddSingleton<ILogger<FailureContext>, MyLogger>()
.AddSingleton<FailureContext>()
.BuildServiceProvider();
var context1 = serviceProvider1.GetService<FailureContext>();
var serviceProvider2 = new ServiceCollection()
.AddSingleton<ILogger<FailureContext>, MyLogger>()
.AddSingleton<FailureContext>()
.BuildServiceProvider();
var context2 = ActivatorUtilities.GetServiceOrCreateInstance<FailureContext>(serviceProvider2); Output:
It also works if only DbContext is registered in D.I.: var serviceProvider1 = new ServiceCollection()
//.AddSingleton<ILogger<FailureContext>, MyLogger>()
.AddSingleton<FailureContext>()
.BuildServiceProvider();
var context1 = serviceProvider1.GetService<FailureContext>();
var serviceProvider2 = new ServiceCollection()
//.AddSingleton<ILogger<FailureContext>, MyLogger>()
.AddSingleton<FailureContext>()
.BuildServiceProvider();
var context2 = ActivatorUtilities.GetServiceOrCreateInstance<FailureContext>(serviceProvider2); In this case the output shows that the parameterless constructor is used:
It also works if var serviceProvider1 = new ServiceCollection()
.AddSingleton<ILogger<FailureContext>, MyLogger>()
//.AddSingleton<FailureContext>()
.BuildServiceProvider();
var context1 = serviceProvider1.GetService<FailureContext>();
var serviceProvider2 = new ServiceCollection()
.AddSingleton<ILogger<FailureContext>, MyLogger>()
//.AddSingleton<FailureContext>()
.BuildServiceProvider();
var context2 = ActivatorUtilities.GetServiceOrCreateInstance<FailureContext>(serviceProvider2); In this case
When this fails is when neither the logger or the logger are registered in D..I: var serviceProvider1 = new ServiceCollection()
//.AddSingleton<ILogger<FailureContext>, MyLogger>()
//.AddSingleton<FailureContext>()
.BuildServiceProvider();
var context1 = serviceProvider1.GetService<FailureContext>();
var serviceProvider2 = new ServiceCollection()
//.AddSingleton<ILogger<FailureContext>, MyLogger>()
//.AddSingleton<FailureContext>()
.BuildServiceProvider();
var context2 = ActivatorUtilities.GetServiceOrCreateInstance<FailureContext>(serviceProvider2);
In this case,
|
Closing as dupe of #46132 since there is more discussion there |
Calling `ActivatorUtilities.CreateInstance` without additional arguments should prefer parameterless constructor as there are no guarantees that parameters, for which arguments were not supplied, can be resolved from the ServiceProvider.
Previously `ActivatorUtilities.CreateInstance` and `ActivatorUtilities.CreateFactory` behaved differently: former depended on ctor definition order to disambiguate ctors, while latter failed altogether. The behavior is now unified and addresses concerns raised in dotnet#45119 dotnet#42339 and dotnet#46132. Constructors are chosen based on the following factors in a given order: 1) Preferred ctor (having [ActivatorUtilitiesConstructor] attribute) 2) Ctor with the most surjective parameters based on the given arguments. In other words constructor that has the least unresolved parameters is chosen. 3) Ctor with more resolved parameters is prefered. If two ctors have all of the supplied arguments, but one of them has additional parameters with default values, then the one with more parameters is chosen.
Original issue: dotnet/efcore#23306
/cc @bricelam @davidfowl
Using these types:
This works for both
GetService<FailureContext>
andActivatorUtilities.GetServiceOrCreateInstance<FailureContext>
when both these types are in D.I.:Output:
It also works if only DbContext is registered in D.I.:
In this case the output shows that the parameterless constructor is used:
It also works if
FailureContext
is not registered in D.I., but the logger is:In this case
GetService
returns null, butGetServiceOrCreateInstance
still chooses the correct constructor.When this fails is when neither the logger or the logger are registered in D..I:
In this case,
GetServiceOrCreateInstance
should choose the parameterless constructor since there is no other constructor that can work, but it it instead fails by attempting to call a constructor for which the parameter cannot be resolved.The text was updated successfully, but these errors were encountered: