Part of #2151 - Remove [Activate] from ViewComponents
This change removes [Activate] from ViewComponents. Accessing context should be done through [ViewComponentContext]. Accessing services should be done though constructor injection.
This commit is contained in:
parent
92dbd8923b
commit
b393191cff
|
|
@ -3,40 +3,43 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ViewComponents
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the <see cref="IViewComponentActivator"/> that is registered by default.
|
||||
/// A default implementation of <see cref="IViewComponentActivator"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <see cref="DefaultViewComponentActivator"/> can provide the current instance of
|
||||
/// <see cref="ViewComponentContext"/> to a public property of a view component marked
|
||||
/// with <see cref="ViewComponentContextAttribute"/>.
|
||||
/// </remarks>
|
||||
public class DefaultViewComponentActivator : IViewComponentActivator
|
||||
{
|
||||
private readonly Func<Type, PropertyActivator<ViewContext>[]> _getPropertiesToActivate;
|
||||
private readonly IDictionary<Type, Func<ViewContext, object>> _valueAccessorLookup;
|
||||
private readonly ConcurrentDictionary<Type, PropertyActivator<ViewContext>[]> _injectActions;
|
||||
private readonly Func<Type, PropertyActivator<ViewComponentContext>[]> _getPropertiesToActivate;
|
||||
private readonly ConcurrentDictionary<Type, PropertyActivator<ViewComponentContext>[]> _injectActions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="DefaultViewComponentActivator"/> class.
|
||||
/// </summary>
|
||||
public DefaultViewComponentActivator()
|
||||
{
|
||||
_valueAccessorLookup = CreateValueAccessorLookup();
|
||||
_injectActions = new ConcurrentDictionary<Type, PropertyActivator<ViewContext>[]>();
|
||||
_injectActions = new ConcurrentDictionary<Type, PropertyActivator<ViewComponentContext>[]>();
|
||||
_getPropertiesToActivate = type =>
|
||||
PropertyActivator<ViewContext>.GetPropertiesToActivate(
|
||||
type, typeof(ActivateAttribute), CreateActivateInfo);
|
||||
PropertyActivator<ViewComponentContext>.GetPropertiesToActivate(
|
||||
type,
|
||||
typeof(ViewComponentContextAttribute),
|
||||
CreateActivateInfo);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Activate([NotNull] object viewComponent, [NotNull] ViewContext context)
|
||||
public virtual void Activate([NotNull] object viewComponent, [NotNull] ViewComponentContext context)
|
||||
{
|
||||
var propertiesToActivate = _injectActions.GetOrAdd(viewComponent.GetType(),
|
||||
_getPropertiesToActivate);
|
||||
var propertiesToActivate = _injectActions.GetOrAdd(
|
||||
viewComponent.GetType(),
|
||||
_getPropertiesToActivate);
|
||||
|
||||
for (var i = 0; i < propertiesToActivate.Length; i++)
|
||||
{
|
||||
|
|
@ -45,44 +48,9 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a lookup dictionary for the values to be activated.
|
||||
/// </summary>
|
||||
/// <returns>Returns a readonly dictionary of the values corresponding to the types.</returns>
|
||||
protected virtual IDictionary<Type, Func<ViewContext, object>> CreateValueAccessorLookup()
|
||||
private PropertyActivator<ViewComponentContext> CreateActivateInfo(PropertyInfo property)
|
||||
{
|
||||
return new Dictionary<Type, Func<ViewContext, object>>
|
||||
{
|
||||
{ typeof(ViewContext), (context) => context },
|
||||
{
|
||||
typeof(ViewDataDictionary),
|
||||
(context) =>
|
||||
{
|
||||
return new ViewDataDictionary(context.ViewData);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private PropertyActivator<ViewContext> CreateActivateInfo(PropertyInfo property)
|
||||
{
|
||||
Func<ViewContext, object> valueAccessor;
|
||||
if (!_valueAccessorLookup.TryGetValue(property.PropertyType, out valueAccessor))
|
||||
{
|
||||
valueAccessor = (viewContext) =>
|
||||
{
|
||||
var serviceProvider = viewContext.HttpContext.RequestServices;
|
||||
var service = serviceProvider.GetRequiredService(property.PropertyType);
|
||||
if (typeof(ICanHasViewContext).IsAssignableFrom(property.PropertyType))
|
||||
{
|
||||
((ICanHasViewContext)service).Contextualize(viewContext);
|
||||
}
|
||||
|
||||
return service;
|
||||
};
|
||||
}
|
||||
|
||||
return new PropertyActivator<ViewContext>(property, valueAccessor);
|
||||
return new PropertyActivator<ViewComponentContext>(property, context => context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -80,7 +80,7 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
|
|||
var component = _typeActivatorCache.CreateInstance<object>(
|
||||
_serviceProvider,
|
||||
context.ViewComponentDescriptor.Type);
|
||||
_viewComponentActivator.Activate(component, context.ViewContext);
|
||||
_viewComponentActivator.Activate(component, context);
|
||||
return component;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
|
|||
/// When implemented in a type, activates an instantiated ViewComponent.
|
||||
/// </summary>
|
||||
/// <param name="viewComponent">The ViewComponent to activate.</param>
|
||||
/// <param name="context">The <see cref="ViewContext"/> for the executing <see cref="ViewComponent"/>.</param>
|
||||
void Activate(object viewComponent, ViewContext context);
|
||||
/// <param name="context">
|
||||
/// The <see cref="ViewComponentContext"/> for the executing <see cref="ViewComponent"/>.
|
||||
/// </param>
|
||||
void Activate(object viewComponent, ViewComponentContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,9 @@ using System.Security.Principal;
|
|||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.Mvc.ViewComponents;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
|
@ -18,9 +20,10 @@ namespace Microsoft.AspNet.Mvc
|
|||
[ViewComponent]
|
||||
public abstract class ViewComponent
|
||||
{
|
||||
private IUrlHelper _url;
|
||||
private dynamic _viewBag;
|
||||
private ViewContext _viewContext;
|
||||
private ViewDataDictionary _viewData;
|
||||
private ViewComponentContext _viewComponentContext;
|
||||
private ICompositeViewEngine _viewEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="HttpContext"/>.
|
||||
|
|
@ -96,62 +99,93 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// <summary>
|
||||
/// Gets or sets the <see cref="IUrlHelper"/>.
|
||||
/// </summary>
|
||||
[Activate]
|
||||
public IUrlHelper Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ViewContext"/>.
|
||||
/// </summary>
|
||||
[Activate]
|
||||
public ViewContext ViewContext
|
||||
public IUrlHelper Url
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_viewContext == null)
|
||||
if (_url == null)
|
||||
{
|
||||
// This should run only for the ViewComponent unit test scenarios
|
||||
_viewContext = new ViewContext();
|
||||
// May be null in unit-testing scenarios.
|
||||
var services = ViewComponentContext.ViewContext?.HttpContext?.RequestServices;
|
||||
_url = services?.GetRequiredService<IUrlHelper>();
|
||||
}
|
||||
|
||||
return _viewContext;
|
||||
return _url;
|
||||
}
|
||||
|
||||
[param: NotNull]
|
||||
set
|
||||
{
|
||||
_viewContext = value;
|
||||
_url = value;
|
||||
}
|
||||
}
|
||||
|
||||
[ViewComponentContext]
|
||||
public ViewComponentContext ViewComponentContext
|
||||
{
|
||||
get
|
||||
{
|
||||
// This should run only for the ViewComponent unit test scenarios.
|
||||
if (_viewComponentContext == null)
|
||||
{
|
||||
_viewComponentContext = new ViewComponentContext();
|
||||
}
|
||||
|
||||
return _viewComponentContext;
|
||||
}
|
||||
|
||||
[param: NotNull]
|
||||
set
|
||||
{
|
||||
_viewComponentContext = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ViewDataDictionary"/>.
|
||||
/// Gets the <see cref="ViewContext"/>.
|
||||
/// </summary>
|
||||
public ViewContext ViewContext
|
||||
{
|
||||
get
|
||||
{
|
||||
return ViewComponentContext.ViewContext;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ViewDataDictionary"/>.
|
||||
/// </summary>
|
||||
[Activate]
|
||||
public ViewDataDictionary ViewData
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_viewData == null)
|
||||
{
|
||||
// This should run only for the ViewComponent unit test scenarios
|
||||
_viewData = new ViewDataDictionary(ViewContext.ViewData);
|
||||
}
|
||||
|
||||
return _viewData;
|
||||
}
|
||||
|
||||
[param: NotNull]
|
||||
set
|
||||
{
|
||||
_viewData = value;
|
||||
return ViewComponentContext.ViewData;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ICompositeViewEngine"/>.
|
||||
/// </summary>
|
||||
[Activate]
|
||||
public ICompositeViewEngine ViewEngine { get; set; }
|
||||
public ICompositeViewEngine ViewEngine
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_viewEngine == null)
|
||||
{
|
||||
// May be null in unit-testing scenarios.
|
||||
var services = ViewComponentContext.ViewContext?.HttpContext?.RequestServices;
|
||||
_viewEngine = services?.GetRequiredService<ICompositeViewEngine>();
|
||||
}
|
||||
|
||||
return _viewEngine;
|
||||
}
|
||||
|
||||
[param: NotNull]
|
||||
set
|
||||
{
|
||||
_viewEngine = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a result which will render HTML encoded text.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,19 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// </summary>
|
||||
public class ViewComponentContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ViewComponentContext"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default constructor is provided for unit test purposes only.
|
||||
/// </remarks>
|
||||
public ViewComponentContext()
|
||||
{
|
||||
ViewComponentDescriptor = new ViewComponentDescriptor();
|
||||
Arguments = new object[0];
|
||||
ViewContext = new ViewContext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ViewComponentContext"/>.
|
||||
/// </summary>
|
||||
|
|
@ -29,32 +42,54 @@ namespace Microsoft.AspNet.Mvc
|
|||
{
|
||||
ViewComponentDescriptor = viewComponentDescriptor;
|
||||
Arguments = arguments;
|
||||
ViewContext = viewContext;
|
||||
Writer = writer;
|
||||
|
||||
// We want to create a defensive copy of the VDD here so that changes done in the VC
|
||||
// aren't visible in the calling view.
|
||||
ViewContext = new ViewContext(
|
||||
viewContext,
|
||||
viewContext.View,
|
||||
new ViewDataDictionary(viewContext.ViewData),
|
||||
writer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the View Component arguments.
|
||||
/// </summary>
|
||||
public object[] Arguments { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ViewComponentDescriptor"/> for the View Component being invoked.
|
||||
/// </summary>
|
||||
public ViewComponentDescriptor ViewComponentDescriptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ViewContext"/>.
|
||||
/// </summary>
|
||||
public ViewContext ViewContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="TextWriter"/> for writing output.
|
||||
/// Gets or sets the View Component arguments.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="IViewComponentHelper.Invoke(string, object[])"/> or a similar overload is used to invoke the
|
||||
/// View Component, then <see cref="Writer"/> will be different than <see cref="ViewContext.Writer"/>.
|
||||
/// The property setter is provided for unit test purposes only.
|
||||
/// </remarks>
|
||||
public TextWriter Writer { get; }
|
||||
public object[] Arguments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ViewComponentDescriptor"/> for the View Component being invoked.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The property setter is provided for unit test purposes only.
|
||||
/// </remarks>
|
||||
public ViewComponentDescriptor ViewComponentDescriptor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ViewContext"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The property setter is provided for unit test purposes only.
|
||||
/// </remarks>
|
||||
public ViewContext ViewContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ViewDataDictionary"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is an alias for <c>ViewContext.ViewData</c>.
|
||||
/// </remarks>
|
||||
public ViewDataDictionary ViewData => ViewContext.ViewData;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="TextWriter"/> for output.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is an alias for <c>ViewContext.Writer</c>.
|
||||
/// </remarks>
|
||||
public TextWriter Writer => ViewContext.Writer;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ViewComponents
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies that a controller property should be set with the current
|
||||
/// <see cref="ViewComponentContext"/> when creating the view component. The property must have a public
|
||||
/// set method.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public class ViewComponentContextAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
public async Task ExecuteAsync([NotNull] ViewComponentContext context)
|
||||
{
|
||||
var viewEngine = ViewEngine ?? ResolveViewEngine(context);
|
||||
var viewData = ViewData ?? context.ViewContext.ViewData;
|
||||
var viewData = ViewData ?? context.ViewData;
|
||||
|
||||
string qualifiedViewName;
|
||||
if (ViewName != null && ViewName.Length > 0 && ViewName[0] == '/')
|
||||
|
|
@ -95,7 +95,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
var childViewContext = new ViewContext(
|
||||
context.ViewContext,
|
||||
view,
|
||||
ViewData ?? context.ViewContext.ViewData,
|
||||
ViewData ?? context.ViewData,
|
||||
context.Writer);
|
||||
|
||||
using (view as IDisposable)
|
||||
|
|
|
|||
|
|
@ -14,10 +14,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
public void ViewComponent_ViewBag_UsesViewData()
|
||||
{
|
||||
// Arrange
|
||||
var viewComponent = new TestViewComponent()
|
||||
{
|
||||
ViewData = new ViewDataDictionary(metadataProvider: new EmptyModelMetadataProvider()),
|
||||
};
|
||||
var viewComponent = new TestViewComponent();
|
||||
|
||||
// Act
|
||||
viewComponent.ViewBag.A = "Alice";
|
||||
|
|
@ -33,10 +30,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
public void ViewComponent_ViewData_StoresDataForViewBag()
|
||||
{
|
||||
// Arrange
|
||||
var viewComponent = new TestViewComponent()
|
||||
{
|
||||
ViewData = new ViewDataDictionary(metadataProvider: new EmptyModelMetadataProvider()),
|
||||
};
|
||||
var viewComponent = new TestViewComponent();
|
||||
|
||||
// Act
|
||||
viewComponent.ViewData["A"] = "Alice";
|
||||
|
|
@ -84,10 +78,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
public void ViewComponent_View_WithEmptyParameter_SetsResultViewWithDefaultViewName()
|
||||
{
|
||||
// Arrange
|
||||
var viewComponent = new TestViewComponent()
|
||||
{
|
||||
ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()),
|
||||
};
|
||||
var viewComponent = new TestViewComponent();
|
||||
|
||||
// Act
|
||||
var actualResult = viewComponent.View();
|
||||
|
|
@ -104,10 +95,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
public void ViewComponent_View_WithViewNameParameter_SetsResultViewWithCustomViewName()
|
||||
{
|
||||
// Arrange
|
||||
var viewComponent = new TestViewComponent()
|
||||
{
|
||||
ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()),
|
||||
};
|
||||
var viewComponent = new TestViewComponent();
|
||||
|
||||
// Act
|
||||
var actualResult = viewComponent.View("CustomViewName");
|
||||
|
|
@ -125,10 +113,8 @@ namespace Microsoft.AspNet.Mvc
|
|||
public void ViewComponent_View_WithModelParameter_SetsResultViewWithDefaultViewNameAndModel()
|
||||
{
|
||||
// Arrange
|
||||
var viewComponent = new TestViewComponent()
|
||||
{
|
||||
ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()),
|
||||
};
|
||||
var viewComponent = new TestViewComponent();
|
||||
|
||||
var model = new object();
|
||||
|
||||
// Act
|
||||
|
|
@ -147,10 +133,8 @@ namespace Microsoft.AspNet.Mvc
|
|||
public void ViewComponent_View_WithViewNameAndModelParameters_SetsResultViewWithCustomViewNameAndModel()
|
||||
{
|
||||
// Arrange
|
||||
var viewComponent = new TestViewComponent()
|
||||
{
|
||||
ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()),
|
||||
};
|
||||
var viewComponent = new TestViewComponent();
|
||||
|
||||
var model = new object();
|
||||
|
||||
// Act
|
||||
|
|
@ -180,7 +164,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
Assert.Empty(viewComponent.ViewContext.ViewData);
|
||||
Assert.NotNull(viewComponent.ViewData);
|
||||
Assert.Empty(viewComponent.ViewData);
|
||||
Assert.NotSame(viewComponent.ViewData, viewComponent.ViewContext.ViewData);
|
||||
Assert.Same(viewComponent.ViewData, viewComponent.ViewContext.ViewData);
|
||||
}
|
||||
|
||||
private class TestViewComponent : ViewComponent
|
||||
|
|
|
|||
|
|
@ -17,135 +17,28 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
|
|||
public class DefaultViewComponentActivatorTests
|
||||
{
|
||||
[Fact]
|
||||
public void DefaultViewComponentActivatorSetsAllPropertiesMarkedAsActivate()
|
||||
public void DefaultViewComponentActivator_ActivatesViewComponentContext()
|
||||
{
|
||||
// Arrange
|
||||
var activator = new DefaultViewComponentActivator();
|
||||
|
||||
var context = new ViewComponentContext();
|
||||
var instance = new TestViewComponent();
|
||||
var helper = Mock.Of<IHtmlHelper<object>>();
|
||||
var serviceProvider = new Mock<IServiceProvider>();
|
||||
serviceProvider.Setup(p => p.GetService(typeof(IHtmlHelper<object>))).Returns(helper);
|
||||
serviceProvider.Setup(p => p.GetService(typeof(ICompositeViewEngine))).Returns(Mock.Of<ICompositeViewEngine>());
|
||||
serviceProvider.Setup(p => p.GetService(typeof(IUrlHelper))).Returns(Mock.Of<IUrlHelper>());
|
||||
var viewContext = GetViewContext(serviceProvider.Object);
|
||||
|
||||
// Act
|
||||
activator.Activate(instance, viewContext);
|
||||
activator.Activate(instance, context);
|
||||
|
||||
// Assert
|
||||
Assert.Same(helper, instance.Html);
|
||||
Assert.Same(viewContext, instance.ViewContext);
|
||||
Assert.IsType<ViewDataDictionary>(instance.ViewData);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultViewComponentActivatorSetsModelAsNull()
|
||||
{
|
||||
// Arrange
|
||||
var activator = new DefaultViewComponentActivator();
|
||||
var helper = Mock.Of<IHtmlHelper<object>>();
|
||||
var serviceProvider = new Mock<IServiceProvider>();
|
||||
serviceProvider.Setup(p => p.GetService(typeof(IHtmlHelper<object>))).Returns(helper);
|
||||
serviceProvider.Setup(p => p.GetService(typeof(ICompositeViewEngine))).Returns(Mock.Of<ICompositeViewEngine>());
|
||||
serviceProvider.Setup(p => p.GetService(typeof(IUrlHelper))).Returns(Mock.Of<IUrlHelper>());
|
||||
var viewContext = GetViewContext(serviceProvider.Object);
|
||||
|
||||
// Act
|
||||
activator.Activate(new TestViewComponent(), viewContext);
|
||||
|
||||
// Assert
|
||||
Assert.Null(viewContext.ViewData.Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultViewComponentActivatorActivatesNonBuiltInTypes()
|
||||
{
|
||||
// Arrange
|
||||
var activator = new DefaultViewComponentActivator();
|
||||
var helper = Mock.Of<IHtmlHelper<object>>();
|
||||
var myTestService = new MyService();
|
||||
var serviceProvider = new Mock<IServiceProvider>();
|
||||
serviceProvider.Setup(p => p.GetService(typeof(IHtmlHelper<object>))).Returns(helper);
|
||||
serviceProvider.Setup(p => p.GetService(typeof(MyService))).Returns(myTestService);
|
||||
serviceProvider.Setup(p => p.GetService(typeof(ICompositeViewEngine))).Returns(Mock.Of<ICompositeViewEngine>());
|
||||
serviceProvider.Setup(p => p.GetService(typeof(IUrlHelper))).Returns(Mock.Of<IUrlHelper>());
|
||||
var viewContext = GetViewContext(serviceProvider.Object);
|
||||
var instance = new TestViewComponentWithCustomDataType();
|
||||
|
||||
// Act
|
||||
activator.Activate(instance, viewContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(myTestService, instance.TestMyServiceObject);
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaulViewComponentActivatorContextualizesService()
|
||||
{
|
||||
// Arrange
|
||||
var activator = new DefaultViewComponentActivator();
|
||||
var instance = new TestClassUsingMyService();
|
||||
var myTestService = new MyService();
|
||||
var serviceProvider = new Mock<IServiceProvider>();
|
||||
serviceProvider.Setup(p => p.GetService(typeof(MyService))).Returns(myTestService);
|
||||
var viewContext = GetViewContext(serviceProvider.Object);
|
||||
|
||||
// Act
|
||||
activator.Activate(instance, viewContext);
|
||||
|
||||
// Assert
|
||||
Assert.Same(myTestService, instance.MyTestService);
|
||||
Assert.Same(viewContext, instance.MyTestService.ViewContext);
|
||||
}
|
||||
|
||||
private ViewContext GetViewContext(IServiceProvider serviceProvider)
|
||||
{
|
||||
var httpContext = new Mock<DefaultHttpContext>();
|
||||
httpContext.SetupGet(c => c.RequestServices)
|
||||
.Returns(serviceProvider);
|
||||
|
||||
var actionContext = new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
|
||||
return new ViewContext(actionContext,
|
||||
Mock.Of<IView>(),
|
||||
new ViewDataDictionary(new EmptyModelMetadataProvider()),
|
||||
null,
|
||||
TextWriter.Null,
|
||||
new HtmlHelperOptions());
|
||||
Assert.Same(context, instance.ViewComponentContext);
|
||||
}
|
||||
|
||||
private class TestViewComponent : ViewComponent
|
||||
{
|
||||
[Activate]
|
||||
public IHtmlHelper<object> Html { get; private set; }
|
||||
|
||||
public Task ExecuteAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class TestViewComponentWithCustomDataType : TestViewComponent
|
||||
{
|
||||
[Activate]
|
||||
public MyService TestMyServiceObject { get; set; }
|
||||
}
|
||||
|
||||
private class MyService : ICanHasViewContext
|
||||
{
|
||||
public ViewContext ViewContext { get; private set; }
|
||||
|
||||
public void Contextualize(ViewContext viewContext)
|
||||
{
|
||||
ViewContext = viewContext;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestClassUsingMyService
|
||||
{
|
||||
[Activate]
|
||||
public MyService MyTestService { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ViewComponentActivator_ActivatesProperties()
|
||||
public async Task ViewComponentActivator_Activates()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
||||
|
|
@ -127,35 +127,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ViewComponentActivator_ActivatesPropertiesAndContextualizesThem()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
||||
var client = server.CreateClient();
|
||||
var expected = "test-value";
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync("http://localhost/View/ConsumeValueComponent?test=test-value");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ViewComponentActivator_ActivatesPropertiesAndContextualizesThem_WhenMultiplePropertiesArePresent()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
||||
var client = server.CreateClient();
|
||||
var expected = "Random Number:4 test-value";
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync("http://localhost/View/ConsumeViewAndValueComponent?test=test-value");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ViewComponentThatCannotBeActivated_ThrowsWhenAttemptedToBeInvoked()
|
||||
|
|
@ -163,8 +134,9 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
// Arrange
|
||||
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
||||
var client = server.CreateClient();
|
||||
var expectedMessage = "No service for type 'ActivatorWebSite.CannotBeActivatedComponent+FakeType' " +
|
||||
"has been registered.";
|
||||
var expectedMessage =
|
||||
"Unable to resolve service for type 'ActivatorWebSite.CannotBeActivatedComponent+FakeType' " +
|
||||
"while attempting to activate 'ActivatorWebSite.CannotBeActivatedComponent'.";
|
||||
|
||||
// Act & Assert
|
||||
var response = await client.GetAsync("http://localhost/View/ConsumeCannotBeActivatedComponent");
|
||||
|
|
|
|||
|
|
@ -8,15 +8,16 @@ namespace ActivatorWebSite
|
|||
[ViewComponent(Name = "CannotBeActivated")]
|
||||
public class CannotBeActivatedComponent : ViewComponent
|
||||
{
|
||||
[Activate]
|
||||
private FakeType Service { get; set; }
|
||||
public CannotBeActivatedComponent(FakeType fakeType)
|
||||
{
|
||||
}
|
||||
|
||||
public IViewComponentResult Invoke()
|
||||
{
|
||||
return Content("Test");
|
||||
}
|
||||
|
||||
private sealed class FakeType
|
||||
public sealed class FakeType
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,12 @@ namespace ActivatorWebSite
|
|||
[ViewComponent(Name = "Number")]
|
||||
public class NumberComponent : ViewComponent
|
||||
{
|
||||
[Activate]
|
||||
public MyService MyTestService { get; set; }
|
||||
public NumberComponent(MyService myTestService)
|
||||
{
|
||||
MyTestService = myTestService;
|
||||
}
|
||||
|
||||
private MyService MyTestService { get; }
|
||||
|
||||
public IViewComponentResult Invoke(string content)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
// 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.AspNet.Mvc;
|
||||
|
||||
namespace ActivatorWebSite
|
||||
{
|
||||
[ViewComponent(Name = "Value")]
|
||||
public class ValueComponent : ViewComponent
|
||||
{
|
||||
[Activate]
|
||||
public ViewService MyViewService { get; set; }
|
||||
|
||||
public IViewComponentResult Invoke()
|
||||
{
|
||||
return Content(MyViewService.GetValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
// 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.AspNet.Mvc;
|
||||
|
||||
namespace ActivatorWebSite
|
||||
{
|
||||
[ViewComponent(Name = "ViewAndValue")]
|
||||
public class ViewAndValueComponent : ViewComponent
|
||||
{
|
||||
[Activate]
|
||||
public MyService MySampleService { get; set; }
|
||||
|
||||
[Activate]
|
||||
public ViewService MyViewService { get; set; }
|
||||
|
||||
public IViewComponentResult Invoke(string content)
|
||||
{
|
||||
return Content(content + ":" + MySampleService.Random + " " + MyViewService.GetValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,16 +31,6 @@ namespace ActivatorWebSite
|
|||
return View();
|
||||
}
|
||||
|
||||
public ViewResult ConsumeValueComponent()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public ViewResult ConsumeViewAndValueComponent()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public ViewResult ConsumeCannotBeActivatedComponent()
|
||||
{
|
||||
return View();
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
@Component.Invoke("Value")
|
||||
|
|
@ -1 +0,0 @@
|
|||
@Component.Invoke("ViewAndValue", "Random Number")
|
||||
|
|
@ -9,11 +9,15 @@ namespace MvcTagHelpersWebSite.Components
|
|||
{
|
||||
public class ProductsViewComponent : ViewComponent
|
||||
{
|
||||
[Activate]
|
||||
public ProductsService ProductsService { get; set; }
|
||||
public ProductsViewComponent(ProductsService productsService, IMemoryCache cache)
|
||||
{
|
||||
ProductsService = productsService;
|
||||
Cache = cache;
|
||||
}
|
||||
|
||||
[Activate]
|
||||
public IMemoryCache Cache { get; set; }
|
||||
private ProductsService ProductsService { get; }
|
||||
|
||||
public IMemoryCache Cache { get; }
|
||||
|
||||
public IViewComponentResult Invoke(string category)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,8 +7,12 @@ namespace RequestServicesWebSite
|
|||
{
|
||||
public class RequestIdViewComponent : ViewComponent
|
||||
{
|
||||
[Activate]
|
||||
public RequestIdService RequestIdService { get; set; }
|
||||
public RequestIdViewComponent(RequestIdService requestIdService)
|
||||
{
|
||||
RequestIdService = requestIdService;
|
||||
}
|
||||
|
||||
private RequestIdService RequestIdService { get; }
|
||||
|
||||
public IViewComponentResult Invoke()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue