Refactor of ViewComponent extensibility

Adds ViewComponentDescriptor and caching (provider, collection,
collectionprovider).

Removes IViewComponentInvokerProvider, simplifies
IViewComponentInvokerFactory.
This commit is contained in:
Ryan Nowak 2015-03-13 17:51:02 -07:00
parent 533474d07c
commit 2b82ee0255
32 changed files with 668 additions and 278 deletions

View File

@ -9,7 +9,7 @@ using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.ViewComponents
{
/// <summary>
/// Represents the <see cref="IViewComponentActivator"/> that is registered by default.

View File

@ -0,0 +1,45 @@
// 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.Linq;
namespace Microsoft.AspNet.Mvc.ViewComponents
{
/// <summary>
/// A default implementation of <see cref="IViewComponentDescriptorCollectionProvider"/>
/// </summary>
public class DefaultViewComponentDescriptorCollectionProvider : IViewComponentDescriptorCollectionProvider
{
private readonly IViewComponentDescriptorProvider _descriptorProvider;
private ViewComponentDescriptorCollection _viewComponents;
/// <summary>
/// Creates a new instance of <see cref="DefaultViewComponentDescriptorCollectionProvider"/>.
/// </summary>
/// <param name="descriptorProvider">The <see cref="IViewComponentDescriptorProvider"/>.</param>
public DefaultViewComponentDescriptorCollectionProvider(IViewComponentDescriptorProvider descriptorProvider)
{
_descriptorProvider = descriptorProvider;
}
/// <inheritdoc />
public ViewComponentDescriptorCollection ViewComponents
{
get
{
if (_viewComponents == null)
{
_viewComponents = GetViewComponents();
}
return _viewComponents;
}
}
private ViewComponentDescriptorCollection GetViewComponents()
{
var descriptors = _descriptorProvider.GetViewComponents();
return new ViewComponentDescriptorCollection(descriptors.ToArray(), version: 0);
}
}
}

View File

@ -0,0 +1,72 @@
// 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.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ViewComponents
{
/// <summary>
/// Default implementation of <see cref="IViewComponentDescriptorProvider"/>.
/// </summary>
public class DefaultViewComponentDescriptorProvider : IViewComponentDescriptorProvider
{
private readonly IAssemblyProvider _assemblyProvider;
/// <summary>
/// Creates a new <see cref="DefaultViewComponentDescriptorProvider"/>.
/// </summary>
/// <param name="assemblyProvider">The <see cref="IAssemblyProvider"/>.</param>
public DefaultViewComponentDescriptorProvider(IAssemblyProvider assemblyProvider)
{
_assemblyProvider = assemblyProvider;
}
/// <inheritdoc />
public virtual IEnumerable<ViewComponentDescriptor> GetViewComponents()
{
var types = GetCandidateTypes();
return types
.Where(IsViewComponentType)
.Select(CreateCandidate);
}
/// <summary>
/// Gets the candidate <see cref="TypeInfo"/> instances. The results of this will be provided to
/// <see cref="IsViewComponentType"/> for filtering.
/// </summary>
/// <returns>A list of <see cref="TypeInfo"/> instances.</returns>
protected virtual IEnumerable<TypeInfo> GetCandidateTypes()
{
var assemblies = _assemblyProvider.CandidateAssemblies;
return assemblies.SelectMany(a => a.ExportedTypes).Select(t => t.GetTypeInfo());
}
/// <summary>
/// Determines whether or not the given <see cref="TypeInfo"/> is a View Component class.
/// </summary>
/// <param name="typeInfo">The <see cref="TypeInfo"/>.</param>
/// <returns>
/// <c>true</c> if <paramref name="typeInfo"/>represents a View Component class, otherwise <c>false</c>.
/// </returns>
protected virtual bool IsViewComponentType([NotNull] TypeInfo typeInfo)
{
return ViewComponentConventions.IsComponent(typeInfo);
}
private static ViewComponentDescriptor CreateCandidate(TypeInfo typeInfo)
{
var candidate = new ViewComponentDescriptor()
{
FullName = ViewComponentConventions.GetComponentFullName(typeInfo),
ShortName = ViewComponentConventions.GetComponentName(typeInfo),
Type = typeInfo.AsType(),
};
return candidate;
}
}
}

View File

@ -13,14 +13,17 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
{
public class DefaultViewComponentHelper : IViewComponentHelper, ICanHasViewContext
{
private readonly IViewComponentDescriptorCollectionProvider _descriptorProvider;
private readonly IViewComponentInvokerFactory _invokerFactory;
private readonly IViewComponentSelector _selector;
private ViewContext _viewContext;
public DefaultViewComponentHelper(
[NotNull] IViewComponentDescriptorCollectionProvider descriptorProvider,
[NotNull] IViewComponentSelector selector,
[NotNull] IViewComponentInvokerFactory invokerFactory)
{
_descriptorProvider = descriptorProvider;
_selector = selector;
_invokerFactory = invokerFactory;
}
@ -30,92 +33,129 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
_viewContext = viewContext;
}
public HtmlString Invoke([NotNull] string name, params object[] args)
public HtmlString Invoke([NotNull] string name, params object[] arguments)
{
var componentType = SelectComponent(name);
return Invoke(componentType, args);
}
var descriptor = SelectComponent(name);
public HtmlString Invoke([NotNull] Type componentType, params object[] args)
{
using (var writer = new StringWriter())
{
InvokeCore(writer, componentType, args);
InvokeCore(writer, descriptor, arguments);
return new HtmlString(writer.ToString());
}
}
public void RenderInvoke([NotNull] string name, params object[] args)
public HtmlString Invoke([NotNull] Type componentType, params object[] arguments)
{
var componentType = SelectComponent(name);
InvokeCore(_viewContext.Writer, componentType, args);
}
var descriptor = SelectComponent(componentType);
public void RenderInvoke([NotNull] Type componentType, params object[] args)
{
InvokeCore(_viewContext.Writer, componentType, args);
}
public async Task<HtmlString> InvokeAsync([NotNull] string name, params object[] args)
{
var componentType = SelectComponent(name);
return await InvokeAsync(componentType, args);
}
public async Task<HtmlString> InvokeAsync([NotNull] Type componentType, params object[] args)
{
using (var writer = new StringWriter())
{
await InvokeCoreAsync(writer, componentType, args);
InvokeCore(writer, descriptor, arguments);
return new HtmlString(writer.ToString());
}
}
public async Task RenderInvokeAsync([NotNull] string name, params object[] args)
public void RenderInvoke([NotNull] string name, params object[] arguments)
{
var componentType = SelectComponent(name);
await InvokeCoreAsync(_viewContext.Writer, componentType, args);
var descriptor = SelectComponent(name);
InvokeCore(_viewContext.Writer, descriptor, arguments);
}
public async Task RenderInvokeAsync([NotNull] Type componentType, params object[] args)
public void RenderInvoke([NotNull] Type componentType, params object[] arguments)
{
await InvokeCoreAsync(_viewContext.Writer, componentType, args);
var descriptor = SelectComponent(componentType);
InvokeCore(_viewContext.Writer, descriptor, arguments);
}
private Type SelectComponent([NotNull] string name)
public async Task<HtmlString> InvokeAsync([NotNull] string name, params object[] arguments)
{
var componentType = _selector.SelectComponent(name);
if (componentType == null)
var descriptor = SelectComponent(name);
using (var writer = new StringWriter())
{
await InvokeCoreAsync(writer, descriptor, arguments);
return new HtmlString(writer.ToString());
}
}
public async Task<HtmlString> InvokeAsync([NotNull] Type componentType, params object[] arguments)
{
var descriptor = SelectComponent(componentType);
using (var writer = new StringWriter())
{
await InvokeCoreAsync(writer, descriptor, arguments);
return new HtmlString(writer.ToString());
}
}
public async Task RenderInvokeAsync([NotNull] string name, params object[] arguments)
{
var descriptor = SelectComponent(name);
await InvokeCoreAsync(_viewContext.Writer, descriptor, arguments);
}
public async Task RenderInvokeAsync([NotNull] Type componentType, params object[] arguments)
{
var descriptor = SelectComponent(componentType);
await InvokeCoreAsync(_viewContext.Writer, descriptor, arguments);
}
private ViewComponentDescriptor SelectComponent(string name)
{
var descriptor = _selector.SelectComponent(name);
if (descriptor == null)
{
throw new InvalidOperationException(Resources.FormatViewComponent_CannotFindComponent(name));
}
return componentType;
return descriptor;
}
private async Task InvokeCoreAsync([NotNull] TextWriter writer, [NotNull] Type componentType, object[] args)
private ViewComponentDescriptor SelectComponent(Type componentType)
{
var invoker = _invokerFactory.CreateInstance(componentType.GetTypeInfo(), args);
var descriptors = _descriptorProvider.ViewComponents;
foreach (var descriptor in descriptors.Items)
{
if (descriptor.Type == componentType)
{
return descriptor;
}
}
throw new InvalidOperationException(Resources.FormatViewComponent_CannotFindComponent(
componentType.FullName));
}
private async Task InvokeCoreAsync(
[NotNull] TextWriter writer,
[NotNull] ViewComponentDescriptor descriptor,
object[] arguments)
{
var invoker = _invokerFactory.CreateInstance(descriptor, arguments);
if (invoker == null)
{
throw new InvalidOperationException(
Resources.FormatViewComponent_IViewComponentFactory_ReturnedNull(componentType));
Resources.FormatViewComponent_IViewComponentFactory_ReturnedNull(descriptor.Type.FullName));
}
var context = new ViewComponentContext(componentType.GetTypeInfo(), _viewContext, writer);
var context = new ViewComponentContext(descriptor, arguments, _viewContext, writer);
await invoker.InvokeAsync(context);
}
private void InvokeCore([NotNull] TextWriter writer, [NotNull] Type componentType, object[] arguments)
private void InvokeCore(
[NotNull] TextWriter writer,
[NotNull] ViewComponentDescriptor descriptor,
object[] arguments)
{
var invoker = _invokerFactory.CreateInstance(componentType.GetTypeInfo(), arguments);
var invoker = _invokerFactory.CreateInstance(descriptor, arguments);
if (invoker == null)
{
throw new InvalidOperationException(
Resources.FormatViewComponent_IViewComponentFactory_ReturnedNull(componentType));
Resources.FormatViewComponent_IViewComponentFactory_ReturnedNull(descriptor.Type.FullName));
}
var context = new ViewComponentContext(componentType.GetTypeInfo(), _viewContext, writer);
var context = new ViewComponentContext(descriptor, arguments, _viewContext, writer);
invoker.Invoke(context);
}
}

View File

@ -7,43 +7,38 @@ using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public class DefaultViewComponentInvoker : IViewComponentInvoker
{
private readonly IServiceProvider _serviceProvider;
private readonly ITypeActivatorCache _typeActivatorCache;
private readonly TypeInfo _componentType;
private readonly IViewComponentActivator _viewComponentActivator;
private readonly object[] _args;
public DefaultViewComponentInvoker(
[NotNull] IServiceProvider serviceProvider,
[NotNull] ITypeActivatorCache typeActivatorCache,
[NotNull] IViewComponentActivator viewComponentActivator,
[NotNull] TypeInfo componentType,
object[] args)
[NotNull] IViewComponentActivator viewComponentActivator)
{
_serviceProvider = serviceProvider;
_typeActivatorCache = typeActivatorCache;
_componentType = componentType;
_viewComponentActivator = viewComponentActivator;
_args = args ?? new object[0];
}
public void Invoke([NotNull] ViewComponentContext context)
{
var method = ViewComponentMethodSelector.FindSyncMethod(_componentType, _args);
var method = ViewComponentMethodSelector.FindSyncMethod(
context.ViewComponentDescriptor.Type.GetTypeInfo(),
context.Arguments);
if (method == null)
{
throw new InvalidOperationException(
Resources.FormatViewComponent_CannotFindMethod(ViewComponentMethodSelector.SyncMethodName));
}
var result = InvokeSyncCore(method, context.ViewContext);
var result = InvokeSyncCore(method, context);
result.Execute(context);
}
@ -51,12 +46,16 @@ namespace Microsoft.AspNet.Mvc
{
IViewComponentResult result;
var asyncMethod = ViewComponentMethodSelector.FindAsyncMethod(_componentType, _args);
var asyncMethod = ViewComponentMethodSelector.FindAsyncMethod(
context.ViewComponentDescriptor.Type.GetTypeInfo(),
context.Arguments);
if (asyncMethod == null)
{
// We support falling back to synchronous if there is no InvokeAsync method, in this case we'll still
// execute the IViewResult asynchronously.
var syncMethod = ViewComponentMethodSelector.FindSyncMethod(_componentType, _args);
var syncMethod = ViewComponentMethodSelector.FindSyncMethod(
context.ViewComponentDescriptor.Type.GetTypeInfo(),
context.Arguments);
if (syncMethod == null)
{
throw new InvalidOperationException(
@ -65,36 +64,38 @@ namespace Microsoft.AspNet.Mvc
}
else
{
result = InvokeSyncCore(syncMethod, context.ViewContext);
result = InvokeSyncCore(syncMethod, context);
}
}
else
{
result = await InvokeAsyncCore(asyncMethod, context.ViewContext);
result = await InvokeAsyncCore(asyncMethod, context);
}
await result.ExecuteAsync(context);
}
private object CreateComponent([NotNull] ViewContext context)
private object CreateComponent([NotNull] ViewComponentContext context)
{
var component = _typeActivatorCache.CreateInstance<object>(_serviceProvider, _componentType.AsType());
_viewComponentActivator.Activate(component, context);
var component = _typeActivatorCache.CreateInstance<object>(
_serviceProvider,
context.ViewComponentDescriptor.Type);
_viewComponentActivator.Activate(component, context.ViewContext);
return component;
}
private async Task<IViewComponentResult> InvokeAsyncCore(
[NotNull] MethodInfo method,
[NotNull] ViewContext context)
[NotNull] ViewComponentContext context)
{
var component = CreateComponent(context);
var result = await ControllerActionExecutor.ExecuteAsync(method, component, _args);
var result = await ControllerActionExecutor.ExecuteAsync(method, component, context.Arguments);
return CoerceToViewComponentResult(result);
}
public IViewComponentResult InvokeSyncCore([NotNull] MethodInfo method, [NotNull] ViewContext context)
public IViewComponentResult InvokeSyncCore([NotNull] MethodInfo method, [NotNull] ViewComponentContext context)
{
var component = CreateComponent(context);
@ -102,7 +103,7 @@ namespace Microsoft.AspNet.Mvc
try
{
result = method.Invoke(component, _args);
result = method.Invoke(component, context.Arguments);
}
catch (TargetInvocationException ex)
{

View File

@ -1,38 +1,39 @@
// 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.Collections.Generic;
using System.Linq;
using System.Reflection;
using System;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public class DefaultViewComponentInvokerFactory : IViewComponentInvokerFactory
{
private readonly IViewComponentInvokerProvider[] _providers;
private readonly IServiceProvider _serviceProvider;
private readonly ITypeActivatorCache _typeActivatorCache;
private readonly IViewComponentActivator _viewComponentActivator;
public DefaultViewComponentInvokerFactory(
IEnumerable<IViewComponentInvokerProvider> providers)
IServiceProvider serviceProvider,
ITypeActivatorCache typeActivatorCache,
IViewComponentActivator viewComponentActivator)
{
_providers = providers.OrderBy(item => item.Order).ToArray();
_serviceProvider = serviceProvider;
_typeActivatorCache = typeActivatorCache;
_viewComponentActivator = viewComponentActivator;
}
public IViewComponentInvoker CreateInstance([NotNull] TypeInfo componentType, object[] args)
/// <inheritdoc />
// We don't currently make use of the descriptor or the arguments here (they are available on the context).
// We might do this some day to cache which method we select, so resist the urge to 'clean' this without
// considering that possibility.
public IViewComponentInvoker CreateInstance(
[NotNull] ViewComponentDescriptor viewComponentDescriptor,
object[] args)
{
var context = new ViewComponentInvokerProviderContext(componentType, args);
foreach (var provider in _providers)
{
provider.OnProvidersExecuting(context);
}
for (var i = _providers.Length - 1; i >= 0; i--)
{
_providers[i].OnProvidersExecuted(context);
}
return context.Result;
return new DefaultViewComponentInvoker(
_serviceProvider,
_typeActivatorCache,
_viewComponentActivator);
}
}
}

View File

@ -1,46 +0,0 @@
// 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 Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public class DefaultViewComponentInvokerProvider : IViewComponentInvokerProvider
{
private readonly IServiceProvider _serviceProvider;
private readonly ITypeActivatorCache _typeActivatorCache;
private readonly IViewComponentActivator _viewComponentActivator;
public DefaultViewComponentInvokerProvider(
IServiceProvider serviceProvider,
ITypeActivatorCache typeActivatorCache,
IViewComponentActivator viewComponentActivator)
{
_serviceProvider = serviceProvider;
_typeActivatorCache = typeActivatorCache;
_viewComponentActivator = viewComponentActivator;
}
public int Order
{
get { return DefaultOrder.DefaultFrameworkSortOrder; }
}
/// <inheritdoc />
public void OnProvidersExecuting([NotNull] ViewComponentInvokerProviderContext context)
{
context.Result = new DefaultViewComponentInvoker(
_serviceProvider,
_typeActivatorCache,
_viewComponentActivator,
context.ComponentType,
context.Arguments);
}
/// <inheritdoc />
public void OnProvidersExecuted([NotNull] ViewComponentInvokerProviderContext context)
{
}
}
}

View File

@ -3,44 +3,41 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.ViewComponents
{
/// <summary>
/// Default implementation of <see cref="IViewComponentSelector"/>.
/// </summary>
public class DefaultViewComponentSelector : IViewComponentSelector
{
private readonly IAssemblyProvider _assemblyProvider;
private readonly IViewComponentDescriptorCollectionProvider _descriptorProvider;
private ViewComponentCandidateCache _cache;
private ViewComponentDescriptorCache _cache;
public DefaultViewComponentSelector(IAssemblyProvider assemblyProvider)
/// <summary>
/// Creates a new <see cref="DefaultViewComponentSelector"/>.
/// </summary>
/// <param name="descriptorProvider">The <see cref="IViewComponentDescriptorCollectionProvider"/>.</param>
public DefaultViewComponentSelector(IViewComponentDescriptorCollectionProvider descriptorProvider)
{
_assemblyProvider = assemblyProvider;
_descriptorProvider = descriptorProvider;
}
public Type SelectComponent([NotNull] string componentName)
/// <inheritdoc />
public ViewComponentDescriptor SelectComponent([NotNull] string componentName)
{
if (_cache == null)
var collection = _descriptorProvider.ViewComponents;
if (_cache == null || _cache.Version != collection.Version)
{
var assemblies = _assemblyProvider.CandidateAssemblies;
var types = assemblies.SelectMany(a => a.DefinedTypes);
var candidates =
types
.Where(IsViewComponentType)
.Select(CreateCandidate)
.ToArray();
_cache = new ViewComponentCandidateCache(candidates);
_cache = new ViewComponentDescriptorCache(collection);
}
// ViewComponent names can either be fully-qualified, or refer to the 'short-name'. If the provided
// name contains a '.' - then it's a fully-qualified name.
var matching = new List<ViewComponentCandidate>();
if (componentName.Contains("."))
{
return _cache.SelectByFullName(componentName);
@ -51,65 +48,34 @@ namespace Microsoft.AspNet.Mvc
}
}
protected virtual bool IsViewComponentType([NotNull] TypeInfo typeInfo)
private class ViewComponentDescriptorCache
{
return ViewComponentConventions.IsComponent(typeInfo);
}
private readonly ILookup<string, ViewComponentDescriptor> _lookupByShortName;
private readonly ILookup<string, ViewComponentDescriptor> _lookupByFullName;
private static ViewComponentCandidate CreateCandidate(TypeInfo typeInfo)
{
var candidate = new ViewComponentCandidate()
public ViewComponentDescriptorCache(ViewComponentDescriptorCollection collection)
{
FullName = ViewComponentConventions.GetComponentFullName(typeInfo),
ShortName = ViewComponentConventions.GetComponentName(typeInfo),
Type = typeInfo.AsType(),
};
Version = collection.Version;
Debug.Assert(!string.IsNullOrEmpty(candidate.FullName));
var separatorIndex = candidate.FullName.LastIndexOf('.');
if (separatorIndex >= 0)
{
candidate.ShortName = candidate.FullName.Substring(separatorIndex + 1);
}
else
{
candidate.ShortName = candidate.FullName;
_lookupByShortName = collection.Items.ToLookup(c => c.ShortName, c => c);
_lookupByFullName = collection.Items.ToLookup(c => c.FullName, c => c);
}
return candidate;
}
public int Version { get; }
private class ViewComponentCandidate
{
public string FullName { get; set; }
public string ShortName { get; set; }
public Type Type { get; set; }
}
private class ViewComponentCandidateCache
{
private readonly ILookup<string, ViewComponentCandidate> _lookupByShortName;
private readonly ILookup<string, ViewComponentCandidate> _lookupByFullName;
public ViewComponentCandidateCache(ViewComponentCandidate[] candidates)
{
_lookupByShortName = candidates.ToLookup(c => c.ShortName, c => c);
_lookupByFullName = candidates.ToLookup(c => c.FullName, c => c);
}
public Type SelectByShortName(string name)
public ViewComponentDescriptor SelectByShortName(string name)
{
return Select(_lookupByShortName, name);
}
public Type SelectByFullName(string name)
public ViewComponentDescriptor SelectByFullName(string name)
{
return Select(_lookupByFullName, name);
}
private static Type Select(ILookup<string, ViewComponentCandidate> candidates, string name)
private static ViewComponentDescriptor Select(
ILookup<string, ViewComponentDescriptor> candidates,
string name)
{
var matches = candidates[name];
@ -120,7 +86,7 @@ namespace Microsoft.AspNet.Mvc
}
else if (count == 1)
{
return matches.Single().Type;
return matches.Single();
}
else
{

View File

@ -1,7 +1,7 @@
// 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.
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.ViewComponents
{
/// <summary>
/// Provides methods to activate an instantiated ViewComponent

View File

@ -0,0 +1,28 @@
// 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.
namespace Microsoft.AspNet.Mvc.ViewComponents
{
/// <summary>
/// Provides the currently cached collection of <see cref="ViewComponentDescriptor"/>.
/// </summary>
/// <remarks>
/// <para>
/// The default implementation does not update the cache, it is up to the user
/// to create or use an implementation that can update the available view components in
/// the application. The implementor is also responsible for updating the
/// <see cref="ViewComponentDescriptorCollection.Version"/> in a thread safe way.
/// </para>
/// <para>
/// Default consumers of this service, are aware of the version and will recache
/// data as appropriate, but rely on the version being unique.
/// </para>
/// </remarks>
public interface IViewComponentDescriptorCollectionProvider
{
/// <summary>
/// Returns the current cached <see cref="ViewComponentDescriptorCollection"/>.
/// </summary>
ViewComponentDescriptorCollection ViewComponents { get; }
}
}

View File

@ -0,0 +1,19 @@
// 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.Collections.Generic;
namespace Microsoft.AspNet.Mvc.ViewComponents
{
/// <summary>
/// Discovers the View Components in the application.
/// </summary>
public interface IViewComponentDescriptorProvider
{
/// <summary>
/// Gets the set of <see cref="ViewComponentDescriptor"/>.
/// </summary>
/// <returns>A list of <see cref="ViewComponentDescriptor"/>.</returns>
IEnumerable<ViewComponentDescriptor> GetViewComponents();
}
}

View File

@ -4,7 +4,7 @@
using System.Threading.Tasks;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public interface IViewComponentInvoker
{

View File

@ -1,13 +1,14 @@
// 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.Reflection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public interface IViewComponentInvokerFactory
{
IViewComponentInvoker CreateInstance([NotNull] TypeInfo componentType, object[] args);
IViewComponentInvoker CreateInstance(
[NotNull] ViewComponentDescriptor viewComponentDescriptor,
object[] args);
}
}

View File

@ -1,14 +0,0 @@
// 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.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public interface IViewComponentInvokerProvider
{
int Order { get; }
void OnProvidersExecuting([NotNull] ViewComponentInvokerProviderContext context);
void OnProvidersExecuted([NotNull] ViewComponentInvokerProviderContext context);
}
}

View File

@ -1,13 +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 System;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.ViewComponents
{
/// <summary>
/// Selects a View Component based on a View Component name.
/// </summary>
public interface IViewComponentSelector
{
Type SelectComponent([NotNull] string componentName);
/// <summary>
/// Selects a View Component based on <paramref name="componentName"/>.
/// </summary>
/// <param name="componentName">The View Component name.</param>
/// <returns>A <see cref="ViewComponentDescriptor"/>, or <c>null</c> if no match is found.</returns>
ViewComponentDescriptor SelectComponent([NotNull] string componentName);
}
}

View File

@ -2,25 +2,59 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Reflection;
using Microsoft.AspNet.Mvc.ViewComponents;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// A context for View Components.
/// </summary>
public class ViewComponentContext
{
public ViewComponentContext([NotNull] TypeInfo componentType, [NotNull] ViewContext viewContext,
/// <summary>
/// Creates a new <see cref="ViewComponentContext"/>.
/// </summary>
/// <param name="viewComponentDescriptor">
/// The <see cref="ViewComponentContext"/> for the View Component being invoked.
/// </param>
/// <param name="arguments">The View Component arguments.</param>
/// <param name="viewContext">The <see cref="ViewContext"/>.</param>
/// <param name="writer">The <see cref="TextWriter"/> for writing output.</param>
public ViewComponentContext(
[NotNull] ViewComponentDescriptor viewComponentDescriptor,
[NotNull] object[] arguments,
[NotNull] ViewContext viewContext,
[NotNull] TextWriter writer)
{
ComponentType = componentType;
ViewComponentDescriptor = viewComponentDescriptor;
Arguments = arguments;
ViewContext = viewContext;
Writer = writer;
}
public TypeInfo ComponentType { get; private set; }
/// <summary>
/// Gets the View Component arguments.
/// </summary>
public object[] Arguments { get; }
public ViewContext ViewContext { get; private set; }
/// <summary>
/// Gets the <see cref="ViewComponentDescriptor"/> for the View Component being invoked.
/// </summary>
public ViewComponentDescriptor ViewComponentDescriptor { get; }
public TextWriter Writer { get; private set; }
/// <summary>
/// Gets the <see cref="ViewContext"/>.
/// </summary>
public ViewContext ViewContext { get; }
/// <summary>
/// Gets the <see cref="TextWriter"/> for writing output.
/// </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"/>.
/// </remarks>
public TextWriter Writer { get; }
}
}

View File

@ -5,7 +5,7 @@ using System;
using System.Reflection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public static class ViewComponentConventions
{

View File

@ -0,0 +1,79 @@
// 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;
namespace Microsoft.AspNet.Mvc.ViewComponents
{
/// <summary>
/// A descriptor for a View Component.
/// </summary>
public class ViewComponentDescriptor
{
/// <summary>
/// Gets or sets the full name.
/// </summary>
/// <remarks>
/// <para>
/// The full name is defaulted to the full namespace of the View Component class, prepended to
/// the the class name with a '.' character as the separator. If the View Component class uses
/// <code>ViewComponent</code> as a suffix, the suffix will be omitted from the <see cref="FullName"/>.
/// </para>
/// <example>
/// Class Name: Contoso.Products.LoginViewComponent
/// View Component FullName: Contoso.Products.Login
/// </example>
/// <example>
/// Class Name: Contoso.Blog.Tags
/// View Component FullName: Contoso.Blog.Tags
/// </example>
/// <para>
/// If <see cref="ViewComponentAttribute.Name"/> is used to set a name, then this will be used as
/// the <see cref="FullName"/>.
/// </para>
/// <example>
/// [ViewComponent(Name = "Contoso.Forum.UsersOnline")]
/// public class OnlineUsersViewComponent
/// {
/// }
/// View Component FullName: Contoso.Forum.UsersOnline
/// </example>
/// </remarks>
public string FullName { get; set; }
/// <summary>
/// Gets or sets the short name.
/// </summary>
/// <remarks>
/// <para>
/// The short name is defaulted to the name of the View Component class. If the View Component class uses
/// <code>ViewComponent</code> as a suffix, the suffix will be omitted from the <see cref="ShortName"/>.
/// </para>
/// <example>
/// Class Name: Contoso.Products.LoginViewComponent
/// View Component ShortName: Login
/// </example>
/// <example>
/// Class Name: Contoso.Blog.Tags
/// View Component ShortName: Tags
/// </example>
/// <para>
/// If <see cref="ViewComponentAttribute.Name"/> is used to set a name, then the last segment of the
/// value (using '.' as a separate) will be used as the <see cref="ShortName"/>.
/// </para>
/// <example>
/// [ViewComponent(Name = "Contoso.Forum.UsersOnline")]
/// public class OnlineUsersViewComponent
/// {
/// }
/// View Component ShortName: UsersOnline
/// </example>
/// </remarks>
public string ShortName { get; set; }
/// <summary>
/// Gets or sets the <see cref="Type"/>.
/// </summary>
public Type Type { get; set; }
}
}

View File

@ -0,0 +1,35 @@
// 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.Collections.Generic;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ViewComponents
{
/// <summary>
/// A cached collection of <see cref="ViewComponentDescriptor" />.
/// </summary>
public class ViewComponentDescriptorCollection
{
/// <summary>
/// Initializes a new instance of the <see cref="ViewComponentDescriptorCollection"/>.
/// </summary>
/// <param name="items">The result of view component discovery</param>
/// <param name="version">The unique version of discovered view components.</param>
public ViewComponentDescriptorCollection([NotNull] IEnumerable<ViewComponentDescriptor> items, int version)
{
Items = new List<ViewComponentDescriptor>(items);
Version = version;
}
/// <summary>
/// Returns the cached <see cref="IReadOnlyList{ViewComponentDescriptor}"/>.
/// </summary>
public IReadOnlyList<ViewComponentDescriptor> Items { get; }
/// <summary>
/// Returns the unique version of the currently cached items.
/// </summary>
public int Version { get; }
}
}

View File

@ -1,23 +0,0 @@
// 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.Reflection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public class ViewComponentInvokerProviderContext
{
public ViewComponentInvokerProviderContext([NotNull] TypeInfo componentType, object[] arguments)
{
ComponentType = componentType;
Arguments = arguments;
}
public object[] Arguments { get; private set; }
public TypeInfo ComponentType { get; private set; }
public IViewComponentInvoker Result { get; set; }
}
}

View File

@ -8,7 +8,7 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public static class ViewComponentMethodSelector
{

View File

@ -5,6 +5,7 @@ using System;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewComponents;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
@ -85,7 +86,7 @@ namespace Microsoft.AspNet.Mvc
qualifiedViewName = string.Format(
CultureInfo.InvariantCulture,
ViewPathFormat,
ViewComponentConventions.GetComponentName(context.ComponentType),
context.ViewComponentDescriptor.ShortName,
ViewName ?? "Default");
}

View File

@ -155,9 +155,11 @@ namespace Microsoft.AspNet.Mvc
// These do caching so they should stay singleton
services.AddSingleton<IViewComponentSelector, DefaultViewComponentSelector>();
services.AddSingleton<IViewComponentActivator, DefaultViewComponentActivator>();
services.AddSingleton<IViewComponentDescriptorCollectionProvider,
DefaultViewComponentDescriptorCollectionProvider>();
services.AddTransient<IViewComponentDescriptorProvider, DefaultViewComponentDescriptorProvider>();
services.AddTransient<IViewComponentInvokerFactory, DefaultViewComponentInvokerFactory>();
services.AddTransient<IViewComponentInvokerProvider, DefaultViewComponentInvokerProvider>();
services.AddTransient<IViewComponentHelper, DefaultViewComponentHelper>();
// Security and Authorization

View File

@ -2,10 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Reflection;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewComponents;
using Microsoft.AspNet.Routing;
using Moq;
using Xunit;
@ -56,7 +56,13 @@ namespace Microsoft.AspNet.Mvc
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
var viewContext = new ViewContext(actionContext, view, viewData, null, TextWriter.Null);
var writer = new StreamWriter(stream) { AutoFlush = true };
var viewComponentContext = new ViewComponentContext(typeof(object).GetTypeInfo(), viewContext, writer);
var viewComponentDescriptor = new ViewComponentDescriptor()
{
Type = typeof(object),
};
var viewComponentContext = new ViewComponentContext(viewComponentDescriptor, new object[0], viewContext, writer);
return viewComponentContext;
}
}

View File

@ -12,7 +12,7 @@ using Microsoft.AspNet.Routing;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public class DefaultViewComponentActivatorTests
{

View File

@ -0,0 +1,96 @@
// 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.Generic;
using System.Linq;
using System.Reflection;
using Xunit;
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public class DefaultViewComponentDescriptorProviderTest
{
[Fact]
public void GetDescriptor_DefaultConventions()
{
// Arrange
var provider = CreateProvider(typeof(ConventionsViewComponent));
// Act
var descriptors = provider.GetViewComponents();
// Assert
var descriptor = Assert.Single(descriptors);
Assert.Equal(typeof(ConventionsViewComponent), descriptor.Type);
Assert.Equal("Microsoft.AspNet.Mvc.ViewComponents.Conventions", descriptor.FullName);
Assert.Equal("Conventions", descriptor.ShortName);
}
[Fact]
public void GetDescriptor_WithAttribute()
{
// Arrange
var provider = CreateProvider(typeof(AttributeViewComponent));
// Act
var descriptors = provider.GetViewComponents();
// Assert
var descriptor = Assert.Single(descriptors);
Assert.Equal(typeof(AttributeViewComponent), descriptor.Type);
Assert.Equal("AttributesAreGreat", descriptor.FullName);
Assert.Equal("AttributesAreGreat", descriptor.ShortName);
}
private class ConventionsViewComponent
{
}
[ViewComponent(Name = "AttributesAreGreat")]
private class AttributeViewComponent
{
}
private DefaultViewComponentDescriptorProvider CreateProvider(Type componentType)
{
return new FilteredViewComponentDescriptorProvider(componentType);
}
// This will only consider types nested inside this class as ViewComponent classes
private class FilteredViewComponentDescriptorProvider : DefaultViewComponentDescriptorProvider
{
public FilteredViewComponentDescriptorProvider(params Type[] allowedTypes)
: base(GetAssemblyProvider())
{
AllowedTypes = allowedTypes;
}
public Type[] AllowedTypes { get; }
protected override bool IsViewComponentType(TypeInfo typeInfo)
{
return AllowedTypes.Contains(typeInfo.AsType());
}
// Need to override this since the default provider does not support private classes.
protected override IEnumerable<TypeInfo> GetCandidateTypes()
{
return
GetAssemblyProvider()
.CandidateAssemblies
.SelectMany(a => a.DefinedTypes)
.Select(t => t.GetTypeInfo());
}
private static IAssemblyProvider GetAssemblyProvider()
{
var assemblyProvider = new FixedSetAssemblyProvider();
assemblyProvider.CandidateAssemblies.Add(
typeof(FilteredViewComponentDescriptorProvider).GetTypeInfo().Assembly);
return assemblyProvider;
}
}
}
}

View File

@ -2,12 +2,13 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Framework.Internal;
using Xunit;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public class DefaultViewComponentSelectorTest
{
@ -21,7 +22,7 @@ namespace Microsoft.AspNet.Mvc
var result = selector.SelectComponent("Suffix");
// Assert
Assert.Equal(typeof(SuffixViewComponent), result);
Assert.Equal(typeof(SuffixViewComponent), result.Type);
}
[Fact]
@ -31,10 +32,10 @@ namespace Microsoft.AspNet.Mvc
var selector = CreateSelector();
// Act
var result = selector.SelectComponent("Microsoft.AspNet.Mvc.Suffix");
var result = selector.SelectComponent("Microsoft.AspNet.Mvc.ViewComponents.Suffix");
// Assert
Assert.Equal(typeof(SuffixViewComponent), result);
Assert.Equal(typeof(SuffixViewComponent), result.Type);
}
[Fact]
@ -47,7 +48,7 @@ namespace Microsoft.AspNet.Mvc
var result = selector.SelectComponent("WithoutSuffix");
// Assert
Assert.Equal(typeof(WithoutSuffix), result);
Assert.Equal(typeof(WithoutSuffix), result.Type);
}
[Fact]
@ -57,10 +58,10 @@ namespace Microsoft.AspNet.Mvc
var selector = CreateSelector();
// Act
var result = selector.SelectComponent("Microsoft.AspNet.Mvc.WithoutSuffix");
var result = selector.SelectComponent("Microsoft.AspNet.Mvc.ViewComponents.WithoutSuffix");
// Assert
Assert.Equal(typeof(WithoutSuffix), result);
Assert.Equal(typeof(WithoutSuffix), result.Type);
}
[Fact]
@ -73,7 +74,7 @@ namespace Microsoft.AspNet.Mvc
var result = selector.SelectComponent("ByAttribute");
// Assert
Assert.Equal(typeof(ByAttribute), result);
Assert.Equal(typeof(ByAttribute), result.Type);
}
[Fact]
@ -86,7 +87,7 @@ namespace Microsoft.AspNet.Mvc
var result = selector.SelectComponent("ByNamingConvention");
// Assert
Assert.Equal(typeof(ByNamingConventionViewComponent), result);
Assert.Equal(typeof(ByNamingConventionViewComponent), result.Type);
}
[Fact]
@ -97,9 +98,9 @@ namespace Microsoft.AspNet.Mvc
var expected =
"The view component name 'Ambiguous' matched multiple types:" + Environment.NewLine +
"Type: 'Microsoft.AspNet.Mvc.DefaultViewComponentSelectorTest+Ambiguous1' - " +
"Type: 'Microsoft.AspNet.Mvc.ViewComponents.DefaultViewComponentSelectorTest+Ambiguous1' - " +
"Name: 'Namespace1.Ambiguous'" + Environment.NewLine +
"Type: 'Microsoft.AspNet.Mvc.DefaultViewComponentSelectorTest+Ambiguous2' - " +
"Type: 'Microsoft.AspNet.Mvc.ViewComponents.DefaultViewComponentSelectorTest+Ambiguous2' - " +
"Name: 'Namespace2.Ambiguous'";
// Act
@ -119,7 +120,7 @@ namespace Microsoft.AspNet.Mvc
var result = selector.SelectComponent("Namespace1.Ambiguous");
// Assert
Assert.Equal(typeof(Ambiguous1), result);
Assert.Equal(typeof(Ambiguous1), result.Type);
}
[Theory]
@ -134,7 +135,7 @@ namespace Microsoft.AspNet.Mvc
var result = selector.SelectComponent(name);
// Assert
Assert.Equal(typeof(FullNameInAttribute), result);
Assert.Equal(typeof(FullNameInAttribute), result.Type);
}
private IViewComponentSelector CreateSelector()
@ -175,9 +176,9 @@ namespace Microsoft.AspNet.Mvc
}
// This will only consider types nested inside this class as ViewComponent classes
private class FilteredViewComponentSelector : DefaultViewComponentSelector
private class FilteredViewComponentDescriptorProvider : DefaultViewComponentDescriptorProvider
{
public FilteredViewComponentSelector()
public FilteredViewComponentDescriptorProvider()
: base(GetAssemblyProvider())
{
AllowedTypes = typeof(DefaultViewComponentSelectorTest).GetNestedTypes(BindingFlags.NonPublic);
@ -190,6 +191,16 @@ namespace Microsoft.AspNet.Mvc
return AllowedTypes.Contains(typeInfo.AsType());
}
// Need to override this since the default provider does not support private classes.
protected override IEnumerable<TypeInfo> GetCandidateTypes()
{
return
GetAssemblyProvider()
.CandidateAssemblies
.SelectMany(a => a.DefinedTypes)
.Select(t => t.GetTypeInfo());
}
private static IAssemblyProvider GetAssemblyProvider()
{
var assemblyProvider = new FixedSetAssemblyProvider();
@ -199,5 +210,14 @@ namespace Microsoft.AspNet.Mvc
return assemblyProvider;
}
}
// This will only consider types nested inside this class as ViewComponent classes
private class FilteredViewComponentSelector : DefaultViewComponentSelector
{
public FilteredViewComponentSelector()
: base(new DefaultViewComponentDescriptorCollectionProvider(new FilteredViewComponentDescriptorProvider()))
{
}
}
}
}

View File

@ -3,10 +3,10 @@
using System;
using System.IO;
using System.Reflection;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewComponents;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Moq;
@ -94,7 +94,13 @@ namespace Microsoft.AspNet.Mvc
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
var viewContext = new ViewContext(actionContext, view, viewData, null, TextWriter.Null);
var writer = new StreamWriter(stream) { AutoFlush = true };
var viewComponentContext = new ViewComponentContext(typeof(object).GetTypeInfo(), viewContext, writer);
var viewComponentDescriptor = new ViewComponentDescriptor()
{
Type = typeof(object),
};
var viewComponentContext = new ViewComponentContext(viewComponentDescriptor, new object[0], viewContext, writer);
return viewComponentContext;
}
}

View File

@ -6,7 +6,7 @@ using System.Reflection;
using Microsoft.AspNet.Mvc.ViewComponentConventionsTestClasses;
using Xunit;
namespace Microsoft.AspNet.Mvc
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public class ViewComponentConventionsTest
{

View File

@ -3,11 +3,11 @@
using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewComponents;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Moq;
@ -301,7 +301,13 @@ namespace Microsoft.AspNet.Mvc
{
var actionContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
var viewContext = new ViewContext(actionContext, view, viewData, null, TextWriter.Null);
var viewComponentContext = new ViewComponentContext(typeof(object).GetTypeInfo(), viewContext, TextWriter.Null);
var viewComponentDescriptor = new ViewComponentDescriptor()
{
Type = typeof(object),
};
var viewComponentContext = new ViewComponentContext(viewComponentDescriptor, new object[0], viewContext, TextWriter.Null);
return viewComponentContext;
}
}

View File

@ -28,7 +28,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
[InlineData(typeof(IActionInvokerProvider), null, -1000)]
[InlineData(typeof(IApiDescriptionProvider), null, -1000)]
[InlineData(typeof(IFilterProvider), null, -1000)]
[InlineData(typeof(IViewComponentInvokerProvider), null, -1000)]
[InlineData(typeof(IActionConstraintProvider), null, -1000)]
[InlineData(typeof(IConfigureOptions<RazorViewEngineOptions>), null, -1000)]
[InlineData(typeof(IConfigureOptions<MvcOptions>), null, -1000)]

View File

@ -7,6 +7,7 @@ using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.ViewComponents;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
namespace MvcSample.Web.Components
@ -34,11 +35,19 @@ namespace MvcSample.Web.Components
{
var result = await InvokeAsync(Count);
var writer = new StringWriter();
var viewComponentDescriptor = new ViewComponentDescriptor()
{
Type = typeof(TagCloudViewComponentTagHelper),
ShortName = "TagCloudViewComponentTagHelper",
FullName = "TagCloudViewComponentTagHelper",
};
await result.ExecuteAsync(
new ViewComponentContext(typeof(TagCloudViewComponentTagHelper).GetTypeInfo(),
ViewContext,
writer));
await result.ExecuteAsync(new ViewComponentContext(
viewComponentDescriptor,
new object[0],
ViewContext,
writer));
output.TagName = null;
output.Content.SetContent(writer.ToString());