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

Commit

Permalink
Part of #2151 - Remove [Activate] from ViewComponents
Browse files Browse the repository at this point in the history
This change removes [Activate] from ViewComponents. Accessing context
should be done through [ViewComponentContext]. Accessing services should
be done though constructor injection.
  • Loading branch information
rynowak committed May 21, 2015
1 parent 4f7f605 commit 361e667
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 225 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,38 @@

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 implemented of <see cref="IViewComponentActivator"/>.
/// </summary>
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++)
{
Expand All @@ -45,44 +43,9 @@ public virtual void Activate([NotNull] object viewComponent, [NotNull] ViewConte
}
}

/// <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()
{
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)
private PropertyActivator<ViewComponentContext> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ private object CreateComponent([NotNull] ViewComponentContext context)
var component = _typeActivatorCache.CreateInstance<object>(
_serviceProvider,
context.ViewComponentDescriptor.Type);
_viewComponentActivator.Activate(component, context.ViewContext);
_viewComponentActivator.Activate(component, context);
return component;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ public interface IViewComponentActivator
/// 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);
}
}
86 changes: 60 additions & 26 deletions src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
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;

Expand All @@ -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"/>.
Expand Down Expand Up @@ -96,63 +99,94 @@ public ModelStateDictionary ModelState
/// <summary>
/// Gets or sets the <see cref="IUrlHelper"/>.
/// </summary>
[Activate]
public IUrlHelper Url { get; set; }
public IUrlHelper Url
{
get
{
if (_url == null)
{
// May be null in unit-testing scenarios.
var services = ViewComponentContext?.ViewContext?.HttpContext?.RequestServices;
_url = services?.GetRequiredService<IUrlHelper>();
}

/// <summary>
/// Gets or sets the <see cref="ViewContext"/>.
/// </summary>
[Activate]
public ViewContext ViewContext
return _url;
}

[param: NotNull]
set
{
_url = value;
}
}

[ViewComponentContext]
public ViewComponentContext ViewComponentContext
{
get
{
if (_viewContext == null)
if (_viewComponentContext == null)
{
// This should run only for the ViewComponent unit test scenarios
_viewContext = new ViewContext();
_viewComponentContext = new ViewComponentContext();
}

return _viewContext;
return _viewComponentContext;
}

[param: NotNull]
set
{
_viewContext = value;
_viewComponentContext = value;
}
}

/// <summary>
/// Gets or sets the <see cref="ViewDataDictionary"/>.
/// Gets the <see cref="ViewContext"/>.
/// </summary>
[Activate]
public ViewContext ViewContext
{
get
{
return ViewComponentContext?.ViewContext;
}
}

/// <summary>
/// Gets or sets the <see cref="ViewDataDictionary"/>.
/// </summary>
public ViewDataDictionary ViewData
{
get
{
if (_viewData == null)
return ViewComponentContext?.ViewData;
}
}

/// <summary>
/// Gets or sets the <see cref="ICompositeViewEngine"/>.
/// </summary>
public ICompositeViewEngine ViewEngine
{
get
{
if (_viewEngine == null)
{
// This should run only for the ViewComponent unit test scenarios
_viewData = new ViewDataDictionary(ViewContext.ViewData);
// May be null in unit-testing scenarios.
var services = ViewComponentContext.ViewContext?.HttpContext?.RequestServices;
_viewEngine = services?.GetRequiredService<ICompositeViewEngine>();
}

return _viewData;
return _viewEngine;
}

[param: NotNull]
set
{
_viewData = value;
_viewEngine = value;
}
}

/// <summary>
/// Gets or sets the <see cref="ICompositeViewEngine"/>.
/// </summary>
[Activate]
public ICompositeViewEngine ViewEngine { get; set; }

/// <summary>
/// Returns a result which will render HTML encoded text.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,22 @@ 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();
Writer = TextWriter.Null;

ViewData = ViewContext.ViewData;
}

/// <summary>
/// Creates a new <see cref="ViewComponentContext"/>.
/// </summary>
Expand All @@ -31,30 +47,54 @@ public ViewComponentContext(
Arguments = arguments;
ViewContext = viewContext;
Writer = writer;

ViewData = ViewContext.ViewData;
}

/// <summary>
/// Gets the View Component arguments.
/// Gets or sets the View Component arguments.
/// </summary>
public object[] Arguments { get; }
/// <remarks>
/// The property setter is provided for unit test purposes only.
/// </remarks>
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 the <see cref="ViewComponentDescriptor"/> for the View Component being invoked.
/// Gets or sets the <see cref="ViewContext"/>.
/// </summary>
public ViewComponentDescriptor ViewComponentDescriptor { get; }
/// <remarks>
/// The property setter is provided for unit test purposes only.
/// </remarks>
public ViewContext ViewContext { get; set; }

/// <summary>
/// Gets the <see cref="ViewContext"/>.
/// Gets or sets the <see cref="ViewDataDictionary"/>.
/// </summary>
public ViewContext ViewContext { get; }
/// <remarks>
/// The property setter is provided for unit test purposes only.
/// </remarks>
public ViewDataDictionary ViewData { get; set; }

/// <summary>
/// Gets the <see cref="TextWriter"/> for writing output.
/// Gets or sets the <see cref="TextWriter"/> for writing output.
/// </summary>
/// <remarks>
/// <para>
/// The property setter is provided for unit test purposes only.
/// </para>
/// <para>
/// <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"/>.
/// </para>
/// </remarks>
public TextWriter Writer { get; }
public TextWriter Writer { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// 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
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class ViewComponentContextAttribute : Attribute
{
}
}
Loading

0 comments on commit 361e667

Please sign in to comment.