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

Runtime error when using the new .NET 8 Keyed Services in ASP.NET Boilerplate #48

Open
DamienLaw opened this issue Mar 27, 2024 · 2 comments
Assignees

Comments

@DamienLaw
Copy link

DamienLaw commented Mar 27, 2024

I encountered the following runtime error when using the new .NET 8 Keyed Services in ASP.NET Boilerplate project.

System.InvalidOperationException: This service descriptor is keyed. Your service provider may not support keyed services.

This can be easily reproduced by adding the following line in Startup.cs file.

public void ConfigureServices(IServiceCollection services)
{
    services.AddKeyedTransient<IMyKeyedService, MyKeyedService>("test");
}

Run the project and you'll be greeted with this:
image

I have tracked the problem to the following method on line 109 and potentially 130:

private static void AddServicesCollection(IWindsorContainer container, IServiceCollection services)
{
foreach (var serviceDescriptor in services)
{
if (serviceDescriptor.ImplementationInstance == container)
{
//Already registered before
continue;
}
RegisterServiceDescriptor(container, serviceDescriptor);
}
}
private static void RegisterServiceDescriptor(IWindsorContainer container, ServiceDescriptor serviceDescriptor)
{
// MS allows the same type to be registered multiple times.
// Castle Windsor throws an exception in that case - it requires an unique name.
// For that reason, we use Guids to ensure uniqueness.
string uniqueName = serviceDescriptor.ServiceType.FullName + "_" + Guid.NewGuid();
// The IsDefault() calls are important because this makes sure that the last service
// is returned in case of multiple registrations. This is by design in the MS library:
// https://github.com/aspnet/DependencyInjection/blob/dev/src/Microsoft.Extensions.DependencyInjection.Specification.Tests/DependencyInjectionSpecificationTests.cs#L254
if (serviceDescriptor.ImplementationType != null)
{
container.Register(
Component.For(serviceDescriptor.ServiceType)
.Named(uniqueName)
.IsDefault()
.ImplementedBy(serviceDescriptor.ImplementationType)
.ConfigureLifecycle(serviceDescriptor.Lifetime));
}
else if (serviceDescriptor.ImplementationFactory != null)
{
var serviceDescriptorRef = serviceDescriptor;
container.Register(
Component.For(serviceDescriptor.ServiceType)
.Named(uniqueName)
.IsDefault()
.UsingFactoryMethod(c => serviceDescriptorRef.ImplementationFactory(c.Resolve<IServiceProvider>()))
.ConfigureLifecycle(serviceDescriptor.Lifetime)
);
}

The statement serviceDescriptor.ImplementationInstance and serviceDescriptor.ImplementationType is throwing the exception. More info can be found here.

I'll just take an excerpt from the issue above:
image

image

The images above is a snippet of dotnet source code where accessing the getter of property ImplementationInstance and ImplementationType will throw the exception under certain condition.

This has been reported in the new abp.io framework.
abpframework/abp#18708

I'm actually trying to use AddStandardResilienceHandler() in Microsoft.Extensions.Http.Resilience before encountering this issue:

services.AddHttpClient<IMyService, MyService>()
    .AddStandardResilienceHandler();
@maliming maliming self-assigned this Mar 28, 2024
@maliming
Copy link
Member

Waiting castleproject/Windsor#668

@TheDutchDev
Copy link

We're running into the same issue here. Would really like to have this PR added asap

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

No branches or pull requests

3 participants