Skip to content

Commit

Permalink
AddTagHelperActivation extension method now resolves the default ITag…
Browse files Browse the repository at this point in the history
…HelperActivator from the IServiceProvider instead of creating the DefaultTagHelperActivor manually; that breaks in ASP.NET Core 3.0 as both DefaultTagHelperActivator and ITypeActivatorCache are now internal types. Fixes #722.
  • Loading branch information
dotnetjunkie committed Sep 18, 2019
1 parent 1f3f085 commit 49145df
Showing 1 changed file with 47 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<ITagHelperActivator>(p => new SimpleInjectorTagHelperActivator(
builder.Container,
selector,
new DefaultTagHelperActivator(p.GetRequiredService<ITypeActivatorCache>())));
(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)
{
Expand Down Expand Up @@ -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);
}
}

0 comments on commit 49145df

Please sign in to comment.