Refactor and cleanup tag helper creation.
* Moved instantiation of tag helpers into DefaultTagHelperActivator. * Introduced ITagHelperFactory for handling the setup of new tag helper instances.
This commit is contained in:
parent
22b37fb83b
commit
680e9bb2d1
|
|
@ -130,6 +130,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
|
||||||
// Only want one ITagHelperActivator so it can cache Type activation information. Types won't conflict.
|
// Only want one ITagHelperActivator so it can cache Type activation information. Types won't conflict.
|
||||||
services.TryAddSingleton<ITagHelperActivator, DefaultTagHelperActivator>();
|
services.TryAddSingleton<ITagHelperActivator, DefaultTagHelperActivator>();
|
||||||
|
services.TryAddSingleton<ITagHelperFactory, DefaultTagHelperFactory>();
|
||||||
|
|
||||||
// Consumed by the Cache tag helper to cache results across the lifetime of the application.
|
// Consumed by the Cache tag helper to cache results across the lifetime of the application.
|
||||||
services.TryAddSingleton<IMemoryCache, MemoryCache>();
|
services.TryAddSingleton<IMemoryCache, MemoryCache>();
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,16 @@ using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
namespace Microsoft.AspNetCore.Mvc.Razor
|
namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides methods to activate properties on a <see cref="ITagHelper"/> instance.
|
/// Provides methods to create a tag helper.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ITagHelperActivator
|
public interface ITagHelperActivator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When implemented in a type, activates an instantiated <see cref="ITagHelper"/>.
|
/// Creates an <see cref="ITagHelper"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TTagHelper">The <see cref="ITagHelper"/> type.</typeparam>
|
/// <typeparam name="TTagHelper">The <see cref="ITagHelper"/> type.</typeparam>
|
||||||
/// <param name="tagHelper">The <typeparamref name="TTagHelper"/> to activate.</param>
|
|
||||||
/// <param name="context">The <see cref="ViewContext"/> for the executing view.</param>
|
/// <param name="context">The <see cref="ViewContext"/> for the executing view.</param>
|
||||||
void Activate<TTagHelper>(TTagHelper tagHelper, ViewContext context) where TTagHelper : ITagHelper;
|
/// <returns>The tag helper.</returns>
|
||||||
|
TTagHelper Create<TTagHelper>(ViewContext context) where TTagHelper : ITagHelper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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 Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides methods to create and initialize tag helpers.
|
||||||
|
/// </summary>
|
||||||
|
public interface ITagHelperFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new tag helper for the specified <paramref name="context"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"><see cref="ViewContext"/> for the executing view.</param>
|
||||||
|
/// <returns>The tag helper.</returns>
|
||||||
|
TTagHelper CreateTagHelper<TTagHelper>(ViewContext context) where TTagHelper : ITagHelper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,79 +2,45 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
|
||||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <summary>
|
||||||
|
/// Default implementation of <see cref="ITagHelperActivator"/>.
|
||||||
|
/// </summary>
|
||||||
public class DefaultTagHelperActivator : ITagHelperActivator
|
public class DefaultTagHelperActivator : ITagHelperActivator
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<Type, PropertyActivator<ViewContext>[]> _injectActions;
|
private readonly ITypeActivatorCache _typeActivatorCache;
|
||||||
private readonly Func<Type, PropertyActivator<ViewContext>[]> _getPropertiesToActivate;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Instantiates a new <see cref="DefaultTagHelperActivator"/> instance.
|
/// Instantiates a new <see cref="DefaultTagHelperActivator"/> instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DefaultTagHelperActivator()
|
/// <param name="typeActivatorCache">The <see cref="ITypeActivatorCache"/>.</param>
|
||||||
|
public DefaultTagHelperActivator(ITypeActivatorCache typeActivatorCache)
|
||||||
{
|
{
|
||||||
_injectActions = new ConcurrentDictionary<Type, PropertyActivator<ViewContext>[]>();
|
if (typeActivatorCache == null)
|
||||||
_getPropertiesToActivate = type =>
|
{
|
||||||
PropertyActivator<ViewContext>.GetPropertiesToActivate(
|
throw new ArgumentNullException(nameof(typeActivatorCache));
|
||||||
type,
|
}
|
||||||
typeof(ViewContextAttribute),
|
|
||||||
CreateActivateInfo);
|
_typeActivatorCache = typeActivatorCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Activate<TTagHelper>(TTagHelper tagHelper, ViewContext context)
|
public TTagHelper Create<TTagHelper>(ViewContext context)
|
||||||
where TTagHelper : ITagHelper
|
where TTagHelper : ITagHelper
|
||||||
{
|
{
|
||||||
if (tagHelper == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(tagHelper));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var propertiesToActivate = _injectActions.GetOrAdd(
|
return _typeActivatorCache.CreateInstance<TTagHelper>(
|
||||||
tagHelper.GetType(),
|
context.HttpContext.RequestServices,
|
||||||
_getPropertiesToActivate);
|
typeof(TTagHelper));
|
||||||
|
|
||||||
for (var i = 0; i < propertiesToActivate.Length; i++)
|
|
||||||
{
|
|
||||||
var activateInfo = propertiesToActivate[i];
|
|
||||||
activateInfo.Activate(tagHelper, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
InitializeTagHelper(tagHelper, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void InitializeTagHelper<TTagHelper>(TTagHelper tagHelper, ViewContext context)
|
|
||||||
where TTagHelper : ITagHelper
|
|
||||||
{
|
|
||||||
// Run any tag helper initializers in the container
|
|
||||||
var serviceProvider = context.HttpContext.RequestServices;
|
|
||||||
var initializers = serviceProvider.GetService<IEnumerable<ITagHelperInitializer<TTagHelper>>>();
|
|
||||||
|
|
||||||
foreach (var initializer in initializers)
|
|
||||||
{
|
|
||||||
initializer.Initialize(tagHelper, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PropertyActivator<ViewContext> CreateActivateInfo(PropertyInfo property)
|
|
||||||
{
|
|
||||||
return new PropertyActivator<ViewContext>(property, viewContext => viewContext);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
// 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.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Default implementation for <see cref="ITagHelperFactory"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class DefaultTagHelperFactory : ITagHelperFactory
|
||||||
|
{
|
||||||
|
private readonly ITagHelperActivator _activator;
|
||||||
|
private readonly ConcurrentDictionary<Type, PropertyActivator<ViewContext>[]> _injectActions;
|
||||||
|
private readonly Func<Type, PropertyActivator<ViewContext>[]> _getPropertiesToActivate;
|
||||||
|
private static readonly Func<PropertyInfo, PropertyActivator<ViewContext>> _createActivateInfo = CreateActivateInfo;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new <see cref="DefaultTagHelperFactory"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="activator">
|
||||||
|
/// The <see cref="ITagHelperActivator"/> used to create tag helper instances.
|
||||||
|
/// </param>
|
||||||
|
public DefaultTagHelperFactory(ITagHelperActivator activator)
|
||||||
|
{
|
||||||
|
if (activator == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(activator));
|
||||||
|
}
|
||||||
|
|
||||||
|
_activator = activator;
|
||||||
|
_injectActions = new ConcurrentDictionary<Type, PropertyActivator<ViewContext>[]>();
|
||||||
|
_getPropertiesToActivate = type =>
|
||||||
|
PropertyActivator<ViewContext>.GetPropertiesToActivate(
|
||||||
|
type,
|
||||||
|
typeof(ViewContextAttribute),
|
||||||
|
_createActivateInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public TTagHelper CreateTagHelper<TTagHelper>(ViewContext context)
|
||||||
|
where TTagHelper : ITagHelper
|
||||||
|
{
|
||||||
|
if (context == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
var tagHelper = _activator.Create<TTagHelper>(context);
|
||||||
|
|
||||||
|
var propertiesToActivate = _injectActions.GetOrAdd(
|
||||||
|
tagHelper.GetType(),
|
||||||
|
_getPropertiesToActivate);
|
||||||
|
|
||||||
|
for (var i = 0; i < propertiesToActivate.Length; i++)
|
||||||
|
{
|
||||||
|
var activateInfo = propertiesToActivate[i];
|
||||||
|
activateInfo.Activate(tagHelper, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
InitializeTagHelper(tagHelper, context);
|
||||||
|
|
||||||
|
return tagHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InitializeTagHelper<TTagHelper>(TTagHelper tagHelper, ViewContext context)
|
||||||
|
where TTagHelper : ITagHelper
|
||||||
|
{
|
||||||
|
// Run any tag helper initializers in the container
|
||||||
|
var serviceProvider = context.HttpContext.RequestServices;
|
||||||
|
var initializers = serviceProvider.GetService<IEnumerable<ITagHelperInitializer<TTagHelper>>>();
|
||||||
|
|
||||||
|
foreach (var initializer in initializers)
|
||||||
|
{
|
||||||
|
initializer.Initialize(tagHelper, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PropertyActivator<ViewContext> CreateActivateInfo(PropertyInfo property)
|
||||||
|
{
|
||||||
|
return new PropertyActivator<ViewContext>(property, viewContext => viewContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,8 +32,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
private readonly HashSet<string> _renderedSections = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
private readonly HashSet<string> _renderedSections = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
private readonly Stack<TagHelperScopeInfo> _tagHelperScopes = new Stack<TagHelperScopeInfo>();
|
private readonly Stack<TagHelperScopeInfo> _tagHelperScopes = new Stack<TagHelperScopeInfo>();
|
||||||
private IUrlHelper _urlHelper;
|
private IUrlHelper _urlHelper;
|
||||||
private ITagHelperActivator _tagHelperActivator;
|
private ITagHelperFactory _tagHelperFactory;
|
||||||
private ITypeActivatorCache _typeActivatorCache;
|
|
||||||
private bool _renderedBody;
|
private bool _renderedBody;
|
||||||
private AttributeInfo _attributeInfo;
|
private AttributeInfo _attributeInfo;
|
||||||
private TagHelperAttributeInfo _tagHelperAttributeInfo;
|
private TagHelperAttributeInfo _tagHelperAttributeInfo;
|
||||||
|
|
@ -122,31 +121,17 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public abstract Task ExecuteAsync();
|
public abstract Task ExecuteAsync();
|
||||||
|
|
||||||
private ITagHelperActivator TagHelperActivator
|
private ITagHelperFactory TagHelperFactory
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_tagHelperActivator == null)
|
if (_tagHelperFactory == null)
|
||||||
{
|
{
|
||||||
var services = ViewContext.HttpContext.RequestServices;
|
var services = ViewContext.HttpContext.RequestServices;
|
||||||
_tagHelperActivator = services.GetRequiredService<ITagHelperActivator>();
|
_tagHelperFactory = services.GetRequiredService<ITagHelperFactory>();
|
||||||
}
|
}
|
||||||
|
|
||||||
return _tagHelperActivator;
|
return _tagHelperFactory;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ITypeActivatorCache TypeActivatorCache
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_typeActivatorCache == null)
|
|
||||||
{
|
|
||||||
var services = ViewContext.HttpContext.RequestServices;
|
|
||||||
_typeActivatorCache = services.GetRequiredService<ITypeActivatorCache>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _typeActivatorCache;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,13 +177,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public TTagHelper CreateTagHelper<TTagHelper>() where TTagHelper : ITagHelper
|
public TTagHelper CreateTagHelper<TTagHelper>() where TTagHelper : ITagHelper
|
||||||
{
|
{
|
||||||
var tagHelper = TypeActivatorCache.CreateInstance<TTagHelper>(
|
return TagHelperFactory.CreateTagHelper<TTagHelper>(ViewContext);
|
||||||
ViewContext.HttpContext.RequestServices,
|
|
||||||
typeof(TTagHelper));
|
|
||||||
|
|
||||||
TagHelperActivator.Activate(tagHelper, ViewContext);
|
|
||||||
|
|
||||||
return tagHelper;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
|
@ -20,125 +19,24 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
{
|
{
|
||||||
public class DefaultTagHelperActivatorTest
|
public class DefaultTagHelperActivatorTest
|
||||||
{
|
{
|
||||||
[Theory]
|
[Fact]
|
||||||
[InlineData("test", 100)]
|
public void CreateTagHelper_InitializesTagHelpers()
|
||||||
[InlineData(null, -1)]
|
|
||||||
public void Activate_InitializesTagHelpers(string name, int number)
|
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var services = new ServiceCollection();
|
var httpContext = new DefaultHttpContext()
|
||||||
var builder = new MvcCoreBuilder(services);
|
|
||||||
builder.InitializeTagHelper<TestTagHelper>((h, vc) =>
|
|
||||||
{
|
{
|
||||||
h.Name = name;
|
RequestServices = new ServiceCollection().BuildServiceProvider()
|
||||||
h.Number = number;
|
};
|
||||||
h.ViewDataValue = vc.ViewData["TestData"];
|
|
||||||
});
|
|
||||||
var httpContext = MakeHttpContext(services.BuildServiceProvider());
|
|
||||||
var viewContext = MakeViewContext(httpContext);
|
var viewContext = MakeViewContext(httpContext);
|
||||||
var viewDataValue = new object();
|
var viewDataValue = new object();
|
||||||
viewContext.ViewData.Add("TestData", viewDataValue);
|
viewContext.ViewData.Add("TestData", viewDataValue);
|
||||||
var activator = new DefaultTagHelperActivator();
|
var activator = new DefaultTagHelperActivator(new TypeActivatorCache());
|
||||||
var helper = new TestTagHelper();
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
activator.Activate(helper, viewContext);
|
var helper = activator.Create<TestTagHelper>(viewContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(name, helper.Name);
|
Assert.NotNull(helper);
|
||||||
Assert.Equal(number, helper.Number);
|
|
||||||
Assert.Same(viewDataValue, helper.ViewDataValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Activate_InitializesTagHelpersAfterActivatingProperties()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var services = new ServiceCollection();
|
|
||||||
var builder = new MvcCoreBuilder(services);
|
|
||||||
builder.InitializeTagHelper<TestTagHelper>((h, _) => h.ViewContext = MakeViewContext(MakeHttpContext()));
|
|
||||||
var httpContext = MakeHttpContext(services.BuildServiceProvider());
|
|
||||||
var viewContext = MakeViewContext(httpContext);
|
|
||||||
var activator = new DefaultTagHelperActivator();
|
|
||||||
var helper = new TestTagHelper();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
activator.Activate(helper, viewContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotSame(viewContext, helper.ViewContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Activate_InitializesTagHelpersWithMultipleInitializers()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var services = new ServiceCollection();
|
|
||||||
var builder = new MvcCoreBuilder(services);
|
|
||||||
builder.InitializeTagHelper<TestTagHelper>((h, vc) =>
|
|
||||||
{
|
|
||||||
h.Name = "Test 1";
|
|
||||||
h.Number = 100;
|
|
||||||
});
|
|
||||||
builder.InitializeTagHelper<TestTagHelper>((h, vc) =>
|
|
||||||
{
|
|
||||||
h.Name += ", Test 2";
|
|
||||||
h.Number += 100;
|
|
||||||
});
|
|
||||||
var httpContext = MakeHttpContext(services.BuildServiceProvider());
|
|
||||||
var viewContext = MakeViewContext(httpContext);
|
|
||||||
var activator = new DefaultTagHelperActivator();
|
|
||||||
var helper = new TestTagHelper();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
activator.Activate(helper, viewContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("Test 1, Test 2", helper.Name);
|
|
||||||
Assert.Equal(200, helper.Number);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Activate_InitializesTagHelpersWithCorrectInitializers()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var services = new ServiceCollection();
|
|
||||||
var builder = new MvcCoreBuilder(services);
|
|
||||||
builder.InitializeTagHelper<TestTagHelper>((h, vc) =>
|
|
||||||
{
|
|
||||||
h.Name = "Test 1";
|
|
||||||
h.Number = 100;
|
|
||||||
});
|
|
||||||
builder.InitializeTagHelper<AnotherTestTagHelper>((h, vc) =>
|
|
||||||
{
|
|
||||||
h.Name = "Test 2";
|
|
||||||
h.Number = 102;
|
|
||||||
});
|
|
||||||
var httpContext = MakeHttpContext(services.BuildServiceProvider());
|
|
||||||
var viewContext = MakeViewContext(httpContext);
|
|
||||||
var activator = new DefaultTagHelperActivator();
|
|
||||||
var testTagHelper = new TestTagHelper();
|
|
||||||
var anotherTestTagHelper = new AnotherTestTagHelper();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
activator.Activate(testTagHelper, viewContext);
|
|
||||||
activator.Activate(anotherTestTagHelper, viewContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("Test 1", testTagHelper.Name);
|
|
||||||
Assert.Equal(100, testTagHelper.Number);
|
|
||||||
Assert.Equal("Test 2", anotherTestTagHelper.Name);
|
|
||||||
Assert.Equal(102, anotherTestTagHelper.Number);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static HttpContext MakeHttpContext(IServiceProvider services = null)
|
|
||||||
{
|
|
||||||
var httpContext = new DefaultHttpContext();
|
|
||||||
if (services != null)
|
|
||||||
{
|
|
||||||
httpContext.RequestServices = services;
|
|
||||||
}
|
|
||||||
return httpContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ViewContext MakeViewContext(HttpContext httpContext)
|
private static ViewContext MakeViewContext(HttpContext httpContext)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,197 @@
|
||||||
|
// 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.IO;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Internal;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
|
{
|
||||||
|
public class DefaultTagHelperFactoryTest
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("test", 100)]
|
||||||
|
[InlineData(null, -1)]
|
||||||
|
public void CreateTagHelper_InitializesTagHelpers(string name, int number)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
var builder = new MvcCoreBuilder(services);
|
||||||
|
builder.InitializeTagHelper<TestTagHelper>((h, vc) =>
|
||||||
|
{
|
||||||
|
h.Name = name;
|
||||||
|
h.Number = number;
|
||||||
|
h.ViewDataValue = vc.ViewData["TestData"];
|
||||||
|
});
|
||||||
|
var httpContext = MakeHttpContext(services.BuildServiceProvider());
|
||||||
|
var viewContext = MakeViewContext(httpContext);
|
||||||
|
var viewDataValue = new object();
|
||||||
|
viewContext.ViewData.Add("TestData", viewDataValue);
|
||||||
|
var factory = CreateFactory();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var helper = factory.CreateTagHelper<TestTagHelper>(viewContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(name, helper.Name);
|
||||||
|
Assert.Equal(number, helper.Number);
|
||||||
|
Assert.Same(viewDataValue, helper.ViewDataValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateTagHelper_InitializesTagHelpersAfterActivatingProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
var builder = new MvcCoreBuilder(services);
|
||||||
|
builder.InitializeTagHelper<TestTagHelper>((h, _) => h.ViewContext = MakeViewContext(MakeHttpContext()));
|
||||||
|
var httpContext = MakeHttpContext(services.BuildServiceProvider());
|
||||||
|
var viewContext = MakeViewContext(httpContext);
|
||||||
|
var factory = CreateFactory();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var helper = factory.CreateTagHelper<TestTagHelper>(viewContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotSame(viewContext, helper.ViewContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateTagHelper_InitializesTagHelpersWithMultipleInitializers()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
var builder = new MvcCoreBuilder(services);
|
||||||
|
builder.InitializeTagHelper<TestTagHelper>((h, vc) =>
|
||||||
|
{
|
||||||
|
h.Name = "Test 1";
|
||||||
|
h.Number = 100;
|
||||||
|
});
|
||||||
|
builder.InitializeTagHelper<TestTagHelper>((h, vc) =>
|
||||||
|
{
|
||||||
|
h.Name += ", Test 2";
|
||||||
|
h.Number += 100;
|
||||||
|
});
|
||||||
|
var httpContext = MakeHttpContext(services.BuildServiceProvider());
|
||||||
|
var viewContext = MakeViewContext(httpContext);
|
||||||
|
var factory = CreateFactory();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var helper = factory.CreateTagHelper<TestTagHelper>(viewContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Test 1, Test 2", helper.Name);
|
||||||
|
Assert.Equal(200, helper.Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateTagHelper_InitializesTagHelpersWithCorrectInitializers()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
var builder = new MvcCoreBuilder(services);
|
||||||
|
builder.InitializeTagHelper<TestTagHelper>((h, vc) =>
|
||||||
|
{
|
||||||
|
h.Name = "Test 1";
|
||||||
|
h.Number = 100;
|
||||||
|
});
|
||||||
|
builder.InitializeTagHelper<AnotherTestTagHelper>((h, vc) =>
|
||||||
|
{
|
||||||
|
h.Name = "Test 2";
|
||||||
|
h.Number = 102;
|
||||||
|
});
|
||||||
|
var httpContext = MakeHttpContext(services.BuildServiceProvider());
|
||||||
|
var viewContext = MakeViewContext(httpContext);
|
||||||
|
|
||||||
|
var activator = new Mock<ITagHelperActivator>();
|
||||||
|
activator
|
||||||
|
.Setup(a => a.Create<TestTagHelper>(It.IsAny<ViewContext>()))
|
||||||
|
.Returns(new TestTagHelper());
|
||||||
|
|
||||||
|
activator
|
||||||
|
.Setup(a => a.Create<AnotherTestTagHelper>(It.IsAny<ViewContext>()))
|
||||||
|
.Returns(new AnotherTestTagHelper());
|
||||||
|
|
||||||
|
var factory = new DefaultTagHelperFactory(activator.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var testTagHelper = factory.CreateTagHelper<TestTagHelper>(viewContext);
|
||||||
|
var anotherTestTagHelper = factory.CreateTagHelper<AnotherTestTagHelper>(viewContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Test 1", testTagHelper.Name);
|
||||||
|
Assert.Equal(100, testTagHelper.Number);
|
||||||
|
Assert.Equal("Test 2", anotherTestTagHelper.Name);
|
||||||
|
Assert.Equal(102, anotherTestTagHelper.Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HttpContext MakeHttpContext(IServiceProvider services = null)
|
||||||
|
{
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
if (services != null)
|
||||||
|
{
|
||||||
|
httpContext.RequestServices = services;
|
||||||
|
}
|
||||||
|
return httpContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DefaultTagHelperFactory CreateFactory()
|
||||||
|
{
|
||||||
|
var activator = new Mock<ITagHelperActivator>();
|
||||||
|
activator.Setup(a => a.Create<TestTagHelper>(It.IsAny<ViewContext>())).Returns(new TestTagHelper());
|
||||||
|
return new DefaultTagHelperFactory(activator.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ViewContext MakeViewContext(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||||
|
var metadataProvider = new EmptyModelMetadataProvider();
|
||||||
|
var viewData = new ViewDataDictionary(metadataProvider);
|
||||||
|
var viewContext = new ViewContext(
|
||||||
|
actionContext,
|
||||||
|
Mock.Of<IView>(),
|
||||||
|
viewData,
|
||||||
|
Mock.Of<ITempDataDictionary>(),
|
||||||
|
TextWriter.Null,
|
||||||
|
new HtmlHelperOptions());
|
||||||
|
|
||||||
|
return viewContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestTagHelper : TagHelper
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "Initial Name";
|
||||||
|
|
||||||
|
public int Number { get; set; } = 1000;
|
||||||
|
|
||||||
|
public object ViewDataValue { get; set; } = new object();
|
||||||
|
|
||||||
|
[ViewContext]
|
||||||
|
public ViewContext ViewContext { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AnotherTestTagHelper : TagHelper
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "Initial Name";
|
||||||
|
|
||||||
|
public int Number { get; set; } = 1000;
|
||||||
|
|
||||||
|
public object ViewDataValue { get; set; } = new object();
|
||||||
|
|
||||||
|
[ViewContext]
|
||||||
|
public ViewContext ViewContext { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -69,11 +69,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
var activator = new RazorPageActivator(new EmptyModelMetadataProvider());
|
var activator = new RazorPageActivator(new EmptyModelMetadataProvider());
|
||||||
var serviceProvider = new Mock<IServiceProvider>();
|
var serviceProvider = new Mock<IServiceProvider>();
|
||||||
var typeActivator = new TypeActivatorCache();
|
var typeActivator = new TypeActivatorCache();
|
||||||
|
var tagHelperActivator = new DefaultTagHelperActivator(typeActivator);
|
||||||
var myService = new MyService();
|
var myService = new MyService();
|
||||||
serviceProvider.Setup(mock => mock.GetService(typeof(MyService)))
|
serviceProvider.Setup(mock => mock.GetService(typeof(MyService)))
|
||||||
.Returns(myService);
|
.Returns(myService);
|
||||||
|
serviceProvider.Setup(mock => mock.GetService(typeof(ITagHelperFactory)))
|
||||||
|
.Returns(new DefaultTagHelperFactory(tagHelperActivator));
|
||||||
serviceProvider.Setup(mock => mock.GetService(typeof(ITagHelperActivator)))
|
serviceProvider.Setup(mock => mock.GetService(typeof(ITagHelperActivator)))
|
||||||
.Returns(new DefaultTagHelperActivator());
|
.Returns(tagHelperActivator);
|
||||||
serviceProvider.Setup(mock => mock.GetService(typeof(ITypeActivatorCache)))
|
serviceProvider.Setup(mock => mock.GetService(typeof(ITypeActivatorCache)))
|
||||||
.Returns(typeActivator);
|
.Returns(typeActivator);
|
||||||
serviceProvider.Setup(mock => mock.GetService(It.Is<Type>(serviceType =>
|
serviceProvider.Setup(mock => mock.GetService(It.Is<Type>(serviceType =>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue