diff --git a/src/Microsoft.AspNet.Mvc.Razor/DefaultTagHelperActivator.cs b/src/Microsoft.AspNet.Mvc.Razor/DefaultTagHelperActivator.cs
new file mode 100644
index 0000000000..6667e86390
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Razor/DefaultTagHelperActivator.cs
@@ -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
+{
+ ///
+ public class DefaultTagHelperActivator : ITagHelperActivator
+ {
+ private readonly ConcurrentDictionary[]> _injectActions;
+ private readonly Func[]> _getPropertiesToActivate;
+
+ ///
+ /// Instantiates a new instance.
+ ///
+ public DefaultTagHelperActivator()
+ {
+ _injectActions = new ConcurrentDictionary[]>();
+ _getPropertiesToActivate = type =>
+ PropertyActivator.GetPropertiesToActivate(
+ type, typeof(ActivateAttribute), CreateActivateInfo);
+ }
+
+ ///
+ 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 CreateActivateInfo(PropertyInfo property)
+ {
+ Func 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(property, valueAccessor);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Razor/ITagHelperActivator.cs b/src/Microsoft.AspNet.Mvc.Razor/ITagHelperActivator.cs
new file mode 100644
index 0000000000..892078f23e
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Razor/ITagHelperActivator.cs
@@ -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
+{
+ ///
+ /// Provides methods to activate properties on a instance.
+ ///
+ public interface ITagHelperActivator
+ {
+ ///
+ /// When implemented in a type, activates an instantiated .
+ ///
+ /// The to activate.
+ /// The for the executing view.
+ void Activate(ITagHelper tagHelper, ViewContext context);
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorPage.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorPage.cs
index afab0e0ed5..cd15dc46af 100644
--- a/src/Microsoft.AspNet.Mvc.Razor/RazorPage.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor/RazorPage.cs
@@ -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();
+ }
+
+ return _tagHelperActivator;
+ }
+ }
+
///
/// Creates and activates a .
///
@@ -139,8 +153,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
var tagHelper = TypeActivator.CreateInstance(ViewContext.HttpContext.RequestServices);
- var hasViewContext = tagHelper as ICanHasViewContext;
- hasViewContext?.Contextualize(ViewContext);
+ TagHelperActivator.Activate(tagHelper, ViewContext);
return tagHelper;
}
diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs
index 9295ce4fc5..16f81c8cb7 100644
--- a/src/Microsoft.AspNet.Mvc/MvcServices.cs
+++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs
@@ -33,6 +33,9 @@ namespace Microsoft.AspNet.Mvc
yield return describe.Transient, MvcOptionsSetup>();
+ // Only want one ITagHelperActivator so it can cache Type activation information. Types won't conflict.
+ yield return describe.Singleton();
+
yield return describe.Transient();
yield return describe.Singleton();
diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageCreateTagHelperTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageCreateTagHelperTest.cs
index e067e62f97..d99b619f8b 100644
--- a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageCreateTagHelperTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageCreateTagHelperTest.cs
@@ -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();
+ var tagHelper = instance.CreateTagHelper();
// Assert
Assert.NotNull(tagHelper.PassedInService);
}
[Fact]
- public void CreateTagHelper_ContextualizesProvidedTagHelperType()
+ public void CreateTagHelper_ActivatesProvidedTagHelperType_Property()
+ {
+ // Arrange
+ var instance = CreateTestRazorPage();
+
+ // Act
+ var tagHelper = instance.CreateTagHelper();
+
+ // Assert
+ Assert.NotNull(tagHelper.ActivatedService);
+ }
+
+ [Fact]
+ public void CreateTagHelper_ActivatesProvidedTagHelperType_PropertyAndConstructor()
+ {
+ // Arrange
+ var instance = CreateTestRazorPage();
+
+ // Act
+ var tagHelper = instance.CreateTagHelper();
+
+ // 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.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; }