Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
[Fixes #4087] Add support for AddTagHelpersAsServices()
Browse files Browse the repository at this point in the history
  • Loading branch information
javiercn committed Mar 30, 2016
1 parent ec83f14 commit 12eeeb3
Show file tree
Hide file tree
Showing 26 changed files with 807 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
/// <summary>
/// An <see cref="ApplicationPart"/> backed by an <see cref="Assembly"/>.
/// </summary>
public class AssemblyPart : ApplicationPart
public class AssemblyPart : ApplicationPart, IApplicationPartTypeProvider
{
/// <summary>
/// Initalizes a new <see cref="AssemblyPart"/> instance.
Expand All @@ -35,5 +35,7 @@ public AssemblyPart(Assembly assembly)
/// Gets the name of the <see cref="ApplicationPart"/>.
/// </summary>
public override string Name => Assembly.GetName().Name;

public IEnumerable<TypeInfo> Types => Assembly.DefinedTypes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Reflection;

namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
/// <summary>
/// Exposes a set of types from an <see cref="ApplicationPart"/>.
/// </summary>
public interface IApplicationPartTypeProvider
{
/// <summary>
/// Gets the list of available types in the <see cref="ApplicationPart"/>.
/// </summary>
IEnumerable<TypeInfo> Types { get; }
}
}
5 changes: 4 additions & 1 deletion src/Microsoft.AspNetCore.Mvc.Razor.Host/MvcRazorHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,12 @@ public MvcRazorHost(string root)
/// Initializes a new instance of <see cref="MvcRazorHost"/> using the specified <paramref name="chunkTreeCache"/>.
/// </summary>
/// <param name="chunkTreeCache">An <see cref="IChunkTreeCache"/> rooted at the application base path.</param>
public MvcRazorHost(IChunkTreeCache chunkTreeCache)
/// <param name="resolver">The <see cref="ITagHelperDescriptorResolver"/> used to resolve tag helpers on razor views.</param>
public MvcRazorHost(IChunkTreeCache chunkTreeCache, ITagHelperDescriptorResolver resolver)
: this(chunkTreeCache, new RazorPathNormalizer())
{

TagHelperDescriptorResolver = resolver;
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
using System.Reflection;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.DependencyInjection.Extensions;

Expand Down Expand Up @@ -41,6 +44,28 @@ public static IMvcBuilder AddRazorOptions(
return builder;
}

/// <summary>
/// Registers tag helpers as services and changes the existing <see cref="ITagHelperActivator"/>
/// for an <see cref="ServiceBasedTagHelperActivator"/>.
/// </summary>
/// <param name="builder">The <see cref="IMvcBuilder"/> instance this method extends.</param>
/// <returns>The <see cref="IMvcBuilder"/> instance this method extends.</returns>
public static IMvcBuilder AddTagHelpersAsServices(this IMvcBuilder builder)
{
var feature = new TagHelperFeature();
builder.PartManager.PopulateFeature(feature);

foreach (var type in feature.TagHelpers.Select(t => t.AsType()))
{
builder.Services.TryAddTransient(type, type);
}

builder.Services.Replace(ServiceDescriptor.Transient<ITagHelperActivator, ServiceBasedTagHelperActivator>());
builder.Services.Replace(ServiceDescriptor.Transient<ITagHelperTypeResolver, FeatureTagHelperTypeResolver>());

return builder;
}

/// <summary>
/// Adds an initialization callback for a given <typeparamref name="TTagHelper"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.AspNetCore.Mvc.Razor.Directives;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.PlatformAbstractions;

namespace Microsoft.Extensions.DependencyInjection
{
Expand All @@ -26,10 +29,19 @@ public static IMvcCoreBuilder AddRazorViewEngine(this IMvcCoreBuilder builder)
}

builder.AddViews();
AddRazorViewEngineFeatureProviders(builder);
AddRazorViewEngineServices(builder.Services);
return builder;
}

private static void AddRazorViewEngineFeatureProviders(IMvcCoreBuilder builder)
{
if (!builder.PartManager.FeatureProviders.OfType<TagHelperFeatureProvider>().Any())
{
builder.PartManager.FeatureProviders.Add(new TagHelperFeatureProvider());
}
}

public static IMvcCoreBuilder AddRazorViewEngine(
this IMvcCoreBuilder builder,
Action<RazorViewEngineOptions> setupAction)
Expand All @@ -45,6 +57,8 @@ public static IMvcCoreBuilder AddRazorViewEngine(
}

builder.AddViews();

AddRazorViewEngineFeatureProviders(builder);
AddRazorViewEngineServices(builder.Services);

if (setupAction != null)
Expand All @@ -55,6 +69,28 @@ public static IMvcCoreBuilder AddRazorViewEngine(
return builder;
}

/// <summary>
/// Registers discovered tag helpers as services and changes the existing <see cref="ITagHelperActivator"/>
/// for an <see cref="ServiceBasedTagHelperActivator"/>.
/// </summary>
/// <param name="builder">The <see cref="IMvcCoreBuilder"/> instance this method extends.</param>
/// <returns>The <see cref="IMvcCoreBuilder"/> instance this method extends.</returns>
public static IMvcCoreBuilder AddTagHelpersAsServices(this IMvcCoreBuilder builder)
{
var feature = new TagHelperFeature();
builder.PartManager.PopulateFeature(feature);

foreach (var type in feature.TagHelpers.Select(t => t.AsType()))
{
builder.Services.TryAddTransient(type, type);
}

builder.Services.Replace(ServiceDescriptor.Transient<ITagHelperActivator, ServiceBasedTagHelperActivator>());
builder.Services.Replace(ServiceDescriptor.Transient<ITagHelperTypeResolver, FeatureTagHelperTypeResolver>());

return builder;
}

/// <summary>
/// Adds an initialization callback for a given <typeparamref name="TTagHelper"/>.
/// </summary>
Expand Down Expand Up @@ -116,14 +152,20 @@ internal static void AddRazorViewEngineServices(IServiceCollection services)
return new DefaultChunkTreeCache(accessor.FileProvider);
}));

services.TryAddTransient<ITagHelperDescriptorResolver, TagHelperDescriptorResolver>();

services.TryAddTransient<ITagHelperTypeResolver, TagHelperTypeResolver>();
services.TryAddTransient<ITagHelperDescriptorFactory>(s => new TagHelperDescriptorFactory(designTime: false));

// Caches compilation artifacts across the lifetime of the application.
services.TryAddSingleton<ICompilerCacheProvider, DefaultCompilerCacheProvider>();

// In the default scenario the following services are singleton by virtue of being initialized as part of
// creating the singleton RazorViewEngine instance.
services.TryAddTransient<IRazorPageFactoryProvider, DefaultRazorPageFactoryProvider>();
services.TryAddTransient<IRazorCompilationService, RazorCompilationService>();
services.TryAddTransient<IMvcRazorHost, MvcRazorHost>();

services.TryAddTransient<IMvcRazorHost,MvcRazorHost>();

// This caches Razor page activation details that are valid for the lifetime of the application.
services.TryAddSingleton<IRazorPageActivator, RazorPageActivator>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
/// <summary>
/// A <see cref="ITagHelperActivator"/> that retrieves tag helpers as services from the request's
/// <see cref="IServiceProvider"/>.
/// </summary>
public class ServiceBasedTagHelperActivator : ITagHelperActivator
{
/// <inheritdoc />
public TTagHelper Create<TTagHelper>(ViewContext context) where TTagHelper : ITagHelper
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}

return context.HttpContext.RequestServices.GetRequiredService<TTagHelper>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;
using System.Reflection;

namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers
{
/// <summary>
/// Resolves tag helper types from the <see cref="ApplicationPartManager.ApplicationParts"/>
/// of the application.
/// </summary>
public class FeatureTagHelperTypeResolver : ITagHelperTypeResolver
{
private readonly ApplicationPartManager _manager;

/// <summary>
/// Initializes a new <see cref="FeatureTagHelperTypeResolver"/> instance.
/// </summary>
/// <param name="manager">The <see cref="ApplicationPartManager"/> of the application.</param>
public FeatureTagHelperTypeResolver(ApplicationPartManager manager)
{
if (manager == null)
{
throw new ArgumentNullException(nameof(manager));
}

_manager = manager;

}

/// <inheritdoc />
public IEnumerable<Type> Resolve(string assemblyName, SourceLocation documentLocation, ErrorSink errorSink)
{
if (assemblyName == null)
{
throw new ArgumentNullException(nameof(assemblyName));
}

if (errorSink == null)
{
throw new ArgumentNullException(nameof(errorSink));
}

var parsedName = new AssemblyName(assemblyName);
Assembly assembly;
try
{
assembly = Assembly.Load(parsedName);
}
catch (Exception ex)
{
errorSink.OnError(
documentLocation,
string.Format(
"Cannot resolve TagHelper containing assembly '{0}'.Error: {1}",
parsedName.Name,
ex.Message),
assemblyName.Length);

return Type.EmptyTypes;
}

var feature = new TagHelperFeature();
_manager.PopulateFeature(feature);

var results = new List<Type>();
for (int i = 0; i < feature.TagHelpers.Count; i++)
{
var tagHelper = feature.TagHelpers[i];
var tagHelperAssembly = tagHelper.Assembly;

if (tagHelperAssembly.Equals(assembly))
{
results.Add(tagHelper.AsType());
}
}

return results;
}
}
}
21 changes: 21 additions & 0 deletions src/Microsoft.AspNetCore.Mvc.Razor/TagHelpers/TagHelperFeature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers
{
/// <summary>
/// The list of tag helper types in an MVC application. The <see cref="TagHelperFeature"/> can be populated
/// using the <see cref="ApplicationPartManager"/> that is available during startup at <see cref="IMvcBuilder.PartManager"/>
/// and <see cref="IMvcCoreBuilder.PartManager"/> or at a later stage by requiring the <see cref="ApplicationPartManager"/>
/// as a dependency in a component.
/// </summary>
public class TagHelperFeature
{
public IList<TypeInfo> TagHelpers { get; } = new List<TypeInfo>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;

namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers
{
/// <summary>
/// Discovers tag helpers from a list of <see cref="ApplicationPart"/> instances.
/// </summary>
public class TagHelperFeatureProvider : IApplicationFeatureProvider<TagHelperFeature>
{
/// <inheritdoc />
public void PopulateFeature(IEnumerable<ApplicationPart> parts, TagHelperFeature feature)
{
foreach (var type in parts.OfType<IApplicationPartTypeProvider>().SelectMany(p => p.Types))
{
if (TagHelperConventions.IsTagHelper(type) && ! feature.TagHelpers.Contains(type))
{
feature.TagHelpers.Add(type);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using Microsoft.AspNetCore.Hosting;
Expand All @@ -12,6 +13,7 @@
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.TestHost;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -74,6 +76,7 @@ protected virtual void InitializeServices(IServiceCollection services)

var manager = new ApplicationPartManager();
manager.ApplicationParts.Add(new AssemblyPart(startupAssembly));

services.AddSingleton(manager);
}

Expand Down
Loading

0 comments on commit 12eeeb3

Please sign in to comment.