Add ITagHelperActivator.
- The TagHelperActivator enables dependency injection via properties and allows access to the ViewContext. - This replaces the ICanHasViewContext mechanism that we had in place before. - Added tests and fixed up existing to work with new format for providing ViewContext. #1258
This commit is contained in:
parent
05c35dd3ba
commit
3dff1ca410
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Reflection;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class DefaultTagHelperActivator : ITagHelperActivator
|
||||
{
|
||||
private readonly ConcurrentDictionary<Type, PropertyActivator<ViewContext>[]> _injectActions;
|
||||
private readonly Func<Type, PropertyActivator<ViewContext>[]> _getPropertiesToActivate;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="DefaultTagHelperActivator"/> instance.
|
||||
/// </summary>
|
||||
public DefaultTagHelperActivator()
|
||||
{
|
||||
_injectActions = new ConcurrentDictionary<Type, PropertyActivator<ViewContext>[]>();
|
||||
_getPropertiesToActivate = type =>
|
||||
PropertyActivator<ViewContext>.GetPropertiesToActivate(
|
||||
type, typeof(ActivateAttribute), CreateActivateInfo);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Activate([NotNull] ITagHelper tagHelper, [NotNull] ViewContext context)
|
||||
{
|
||||
var propertiesToActivate = _injectActions.GetOrAdd(tagHelper.GetType(),
|
||||
_getPropertiesToActivate);
|
||||
|
||||
for (var i = 0; i < propertiesToActivate.Length; i++)
|
||||
{
|
||||
var activateInfo = propertiesToActivate[i];
|
||||
activateInfo.Activate(tagHelper, context);
|
||||
}
|
||||
}
|
||||
|
||||
private static PropertyActivator<ViewContext> CreateActivateInfo(PropertyInfo property)
|
||||
{
|
||||
Func<ViewContext, object> valueAccessor;
|
||||
|
||||
if (property.PropertyType == typeof(ViewContext))
|
||||
{
|
||||
valueAccessor = viewContext => viewContext;
|
||||
}
|
||||
else
|
||||
{
|
||||
valueAccessor = (viewContext) =>
|
||||
{
|
||||
var serviceProvider = viewContext.HttpContext.RequestServices;
|
||||
var service = serviceProvider.GetService(property.PropertyType);
|
||||
|
||||
var contextable = service as ICanHasViewContext;
|
||||
contextable?.Contextualize(viewContext);
|
||||
|
||||
return service;
|
||||
};
|
||||
}
|
||||
|
||||
return new PropertyActivator<ViewContext>(property, valueAccessor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to activate properties on a <see cref="ITagHelper"/> instance.
|
||||
/// </summary>
|
||||
public interface ITagHelperActivator
|
||||
{
|
||||
/// <summary>
|
||||
/// When implemented in a type, activates an instantiated <see cref="ITagHelper"/>.
|
||||
/// </summary>
|
||||
/// <param name="tagHelper">The <see cref="ITagHelper"/> to activate.</param>
|
||||
/// <param name="context">The <see cref="ViewContext"/> for the executing view.</param>
|
||||
void Activate(ITagHelper tagHelper, ViewContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
private TextWriter _originalWriter;
|
||||
private IUrlHelper _urlHelper;
|
||||
private ITypeActivator _typeActivator;
|
||||
private ITagHelperActivator _tagHelperActivator;
|
||||
private bool _renderedBody;
|
||||
|
||||
public RazorPage()
|
||||
|
|
@ -126,6 +127,19 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
}
|
||||
|
||||
private ITagHelperActivator TagHelperActivator
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_tagHelperActivator == null)
|
||||
{
|
||||
_tagHelperActivator = ViewContext.HttpContext.RequestServices.GetService<ITagHelperActivator>();
|
||||
}
|
||||
|
||||
return _tagHelperActivator;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and activates a <see cref="ITagHelper"/>.
|
||||
/// </summary>
|
||||
|
|
@ -139,8 +153,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
var tagHelper = TypeActivator.CreateInstance<TTagHelper>(ViewContext.HttpContext.RequestServices);
|
||||
|
||||
var hasViewContext = tagHelper as ICanHasViewContext;
|
||||
hasViewContext?.Contextualize(ViewContext);
|
||||
TagHelperActivator.Activate(tagHelper, ViewContext);
|
||||
|
||||
return tagHelper;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ namespace Microsoft.AspNet.Mvc
|
|||
|
||||
yield return describe.Transient<IOptionsAction<MvcOptions>, MvcOptionsSetup>();
|
||||
|
||||
// Only want one ITagHelperActivator so it can cache Type activation information. Types won't conflict.
|
||||
yield return describe.Singleton<ITagHelperActivator, DefaultTagHelperActivator>();
|
||||
|
||||
yield return describe.Transient<IControllerFactory, DefaultControllerFactory>();
|
||||
yield return describe.Singleton<IControllerActivator, DefaultControllerActivator>();
|
||||
|
||||
|
|
|
|||
|
|
@ -31,20 +31,47 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateTagHelper_ActivatesProvidedTagHelperType()
|
||||
public void CreateTagHelper_ActivatesProvidedTagHelperType_Constructor()
|
||||
{
|
||||
// Arrange
|
||||
var instance = CreateTestRazorPage();
|
||||
|
||||
// Act
|
||||
var tagHelper = instance.CreateTagHelper<ServiceTagHelper>();
|
||||
var tagHelper = instance.CreateTagHelper<ConstructorServiceTagHelper>();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(tagHelper.PassedInService);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateTagHelper_ContextualizesProvidedTagHelperType()
|
||||
public void CreateTagHelper_ActivatesProvidedTagHelperType_Property()
|
||||
{
|
||||
// Arrange
|
||||
var instance = CreateTestRazorPage();
|
||||
|
||||
// Act
|
||||
var tagHelper = instance.CreateTagHelper<ActivateAttributeServiceTagHelper>();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(tagHelper.ActivatedService);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateTagHelper_ActivatesProvidedTagHelperType_PropertyAndConstructor()
|
||||
{
|
||||
// Arrange
|
||||
var instance = CreateTestRazorPage();
|
||||
|
||||
// Act
|
||||
var tagHelper = instance.CreateTagHelper<AttributeConstructorServiceTagHelper>();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(tagHelper.ActivatedService);
|
||||
Assert.NotNull(tagHelper.PassedInService);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateTagHelper_ProvidesTagHelperTypeWithViewContext()
|
||||
{
|
||||
// Arrange
|
||||
var instance = CreateTestRazorPage();
|
||||
|
|
@ -57,7 +84,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateTagHelper_ContextualizesAndActivatesProvidedTagHelperType()
|
||||
public void CreateTagHelper_ProvidesTagHelperTypeWithViewContextAndActivates()
|
||||
{
|
||||
// Arrange
|
||||
var instance = CreateTestRazorPage();
|
||||
|
|
@ -80,6 +107,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
.Returns(myService);
|
||||
serviceProvider.Setup(mock => mock.GetService(typeof(ITypeActivator)))
|
||||
.Returns(typeActivator);
|
||||
serviceProvider.Setup(mock => mock.GetService(typeof(ITagHelperActivator)))
|
||||
.Returns(new DefaultTagHelperActivator());
|
||||
var httpContext = new Mock<HttpContext>();
|
||||
httpContext.SetupGet(c => c.RequestServices)
|
||||
.Returns(serviceProvider.Object);
|
||||
|
|
@ -109,26 +138,41 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
}
|
||||
|
||||
private class ServiceTagHelper : TagHelper
|
||||
private class ConstructorServiceTagHelper : TagHelper
|
||||
{
|
||||
public MyService PassedInService { get; set; }
|
||||
|
||||
public ServiceTagHelper(MyService service)
|
||||
public ConstructorServiceTagHelper(MyService service)
|
||||
{
|
||||
PassedInService = service;
|
||||
}
|
||||
}
|
||||
|
||||
private class ViewContextTagHelper : TagHelper, ICanHasViewContext
|
||||
private class ActivateAttributeServiceTagHelper : TagHelper
|
||||
{
|
||||
public ViewContext ViewContext { get; set; }
|
||||
[Activate]
|
||||
public MyService ActivatedService { get; set; }
|
||||
}
|
||||
|
||||
public void Contextualize([NotNull]ViewContext viewContext)
|
||||
private class AttributeConstructorServiceTagHelper : TagHelper
|
||||
{
|
||||
[Activate]
|
||||
public MyService ActivatedService { get; set; }
|
||||
|
||||
public MyService PassedInService { get; set; }
|
||||
|
||||
public AttributeConstructorServiceTagHelper(MyService service)
|
||||
{
|
||||
ViewContext = viewContext;
|
||||
PassedInService = service;
|
||||
}
|
||||
}
|
||||
|
||||
private class ViewContextTagHelper : TagHelper
|
||||
{
|
||||
[Activate]
|
||||
public ViewContext ViewContext { get; set; }
|
||||
}
|
||||
|
||||
private class ViewContextServiceTagHelper : ViewContextTagHelper
|
||||
{
|
||||
public MyService PassedInService { get; set; }
|
||||
|
|
|
|||
Loading…
Reference in New Issue