Skip to content

Commit

Permalink
Improved the AddCustomTagHelperActivation.
Browse files Browse the repository at this point in the history
Since MVC contains many tag helpers out of the box, custom activation
should not blindly redirect the creation of tag helpers to the
application container, since the application container will not be able
to build up a framework tag helper (with its dependencies). A predicate
argument has been added to the AddCustomTagHelperActivation to allow
control which tag helpers should be built-up by the application
container, and which should be pulled in from the built-in DI system.
  • Loading branch information
dotnetjunkie committed Nov 27, 2016
1 parent b149ab0 commit 97d9261
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 9 deletions.
40 changes: 32 additions & 8 deletions src/MissingDIExtensions/AspNetCoreMvcExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using System;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewComponents;
using Microsoft.AspNetCore.Razor.TagHelpers;
Expand All @@ -31,12 +34,24 @@ public static void AddCustomViewComponentActivation(this IServiceCollection serv
services.AddSingleton<IViewComponentActivator>(new DelegatingViewComponentActivator(activator));
}

public static void AddCustomTagHelperActivation(this IServiceCollection services, Func<Type, object> activator)
public static void AddCustomTagHelperActivation(this IServiceCollection services, Func<Type, object> activator,
Predicate<Type> applicationTypeSelector = null)
{
if (services == null) throw new ArgumentNullException(nameof(services));
if (activator == null) throw new ArgumentNullException(nameof(activator));

services.AddSingleton<ITagHelperActivator>(new DelegatingTagHelperActivator(activator));
// There are tag helpers OOTB in MVC. Letting the application container try to create them will fail
// because of the dependencies these tag helpers have. This means that OOTB tag helpers need to remain
// created by the framework's DefaultTagHelperActivator, hence the selector predicate.
applicationTypeSelector =
applicationTypeSelector ?? (type => !type.GetTypeInfo().Namespace.StartsWith("Microsoft"));

services.AddSingleton<ITagHelperActivator>(provider =>
new DelegatingTagHelperActivator(
customCreatorSelector: applicationTypeSelector,
customTagHelperCreator: activator,
defaultTagHelperActivator:
new DefaultTagHelperActivator(provider.GetRequiredService<ITypeActivatorCache>())));
}

public static Type[] GetControllerTypes(this IApplicationBuilder builder)
Expand Down Expand Up @@ -104,16 +119,25 @@ public void Release(ViewComponentContext context, object viewComponent) =>

internal sealed class DelegatingTagHelperActivator : ITagHelperActivator
{
private readonly Func<Type, object> tagHelperCreator;
private readonly Predicate<Type> customCreatorSelector;
private readonly Func<Type, object> customTagHelperCreator;
private readonly ITagHelperActivator defaultTagHelperActivator;

public DelegatingTagHelperActivator(Func<Type, object> tagHelperCreator)
public DelegatingTagHelperActivator(Predicate<Type> customCreatorSelector, Func<Type, object> customTagHelperCreator,
ITagHelperActivator defaultTagHelperActivator)
{
if (tagHelperCreator == null) throw new ArgumentNullException(nameof(tagHelperCreator));
if (customCreatorSelector == null) throw new ArgumentNullException(nameof(customCreatorSelector));
if (customTagHelperCreator == null) throw new ArgumentNullException(nameof(customTagHelperCreator));
if (defaultTagHelperActivator == null) throw new ArgumentNullException(nameof(defaultTagHelperActivator));

this.tagHelperCreator = tagHelperCreator;
this.customCreatorSelector = customCreatorSelector;
this.customTagHelperCreator = customTagHelperCreator;
this.defaultTagHelperActivator = defaultTagHelperActivator;
}

public TTagHelper Create<TTagHelper>(ViewContext context) where TTagHelper : ITagHelper =>
(TTagHelper)this.tagHelperCreator(typeof(TTagHelper));
this.customCreatorSelector(typeof(TTagHelper))
? (TTagHelper)this.customTagHelperCreator(typeof(TTagHelper))
: defaultTagHelperActivator.Create<TTagHelper>(context);
}
}
}
6 changes: 5 additions & 1 deletion src/SampleApplication.SimpleInjector/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
Expand Down Expand Up @@ -28,7 +29,7 @@ public Startup(IHostingEnvironment env)

public IConfigurationRoot Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
Expand All @@ -39,6 +40,7 @@ public void ConfigureServices(IServiceCollection services)
services.AddRequestScopingMiddleware(container.BeginExecutionContextScope);
services.AddCustomControllerActivation(container.GetInstance);
services.AddCustomViewComponentActivation(container.GetInstance);
services.AddCustomTagHelperActivation(this.container.GetInstance, IsApplicationType);
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand Down Expand Up @@ -79,6 +81,8 @@ private void RegisterApplicationComponents(IApplicationBuilder app, ILoggerFacto

container.Verify();
}

private static bool IsApplicationType(Type type) => type.GetTypeInfo().Namespace.StartsWith("SampleApplication");
}

public interface IUserService { }
Expand Down

0 comments on commit 97d9261

Please sign in to comment.