diff --git a/src/SimpleInjector.Integration.AspNetCore.Mvc/SimpleInjectorAspNetCoreBuilderMvcExtensions.cs b/src/SimpleInjector.Integration.AspNetCore.Mvc/SimpleInjectorAspNetCoreBuilderMvcExtensions.cs index 315634324..63423a52b 100644 --- a/src/SimpleInjector.Integration.AspNetCore.Mvc/SimpleInjectorAspNetCoreBuilderMvcExtensions.cs +++ b/src/SimpleInjector.Integration.AspNetCore.Mvc/SimpleInjectorAspNetCoreBuilderMvcExtensions.cs @@ -9,9 +9,7 @@ namespace SimpleInjector using System.Linq; using System.Reflection; using Microsoft.AspNetCore.Mvc.ApplicationParts; - using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.AspNetCore.Mvc.Razor; - using Microsoft.AspNetCore.Mvc.Razor.Internal; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.Extensions.DependencyInjection; @@ -76,16 +74,38 @@ public static SimpleInjectorAspNetCoreBuilder AddTagHelperActivation( var manager = GetApplicationPartManager(builder.Services, nameof(AddTagHelperActivation)); + var tagHelperDescriptor = GetTagHelperActivatorDescriptor(builder); + builder.Container.RegisterTagHelpers(manager, selector); builder.Services.AddSingleton(p => new SimpleInjectorTagHelperActivator( builder.Container, selector, - new DefaultTagHelperActivator(p.GetRequiredService()))); + (ITagHelperActivator)p.GetInstance(tagHelperDescriptor))); return builder; } + private static ServiceDescriptor GetTagHelperActivatorDescriptor( + SimpleInjectorAspNetCoreBuilder builder) + { + return FindServiceDescriptor(builder.Services, typeof(ITagHelperActivator)) ?? + throw new InvalidOperationException( + string.Format( + CultureInfo.InvariantCulture, + "A registration for the {0} is missing from the ASP.NET Core configuration " + + "system. This is most likely caused by a missing call to either " + + "services.AddRazorPages(), " + + "services.AddControllersWithViews(), " + + "services.AddViewLocalization(), or " + + "services.AddRazorViewEngine() " + + "as part of the ConfigureServices(IServiceCollection) method of " + + "the Startup class. A call to one of those methods will ensure the registration " + + "of the {1}. The default {1} registration is used as a fallback.", + typeof(ITagHelperActivator).FullName, + typeof(ITagHelperActivator).Name)); + } + private static ApplicationPartManager GetApplicationPartManager( this IServiceCollection services, string methodName) { @@ -170,5 +190,29 @@ private static Registration CreateConcreteRegistration(Container container, Type return lifestyle.CreateRegistration(concreteType, container); } + + private static ServiceDescriptor? FindServiceDescriptor(IServiceCollection services, Type serviceType) + { + // In case there are multiple descriptors for a given type, .NET Core will use the last + // descriptor when one instance is resolved. We will have to get this last one as well. + ServiceDescriptor? descriptor = services.LastOrDefault(d => d.ServiceType == serviceType); + + if (descriptor == null && serviceType.GetTypeInfo().IsGenericType) + { + // In case the registration is made as open-generic type, the previous query will return + // null, and we need to go find the last open generic registration for the service type. + var serviceTypeDefinition = serviceType.GetTypeInfo().GetGenericTypeDefinition(); + descriptor = services.LastOrDefault(d => d.ServiceType == serviceTypeDefinition); + } + + return descriptor; + } + + private static object GetInstance(this IServiceProvider provider, ServiceDescriptor descriptor) => + descriptor.ImplementationInstance != null + ? descriptor.ImplementationInstance + : descriptor.ImplementationType != null + ? ActivatorUtilities.GetServiceOrCreateInstance(provider, descriptor.ImplementationType) + : descriptor.ImplementationFactory(provider); } } \ No newline at end of file