diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentActivator.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentActivator.cs index d5f9152db7..c79b9993bf 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentActivator.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentActivator.cs @@ -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 { /// /// Represents the that is registered by default. diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentDescriptorCollectionProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentDescriptorCollectionProvider.cs new file mode 100644 index 0000000000..ba42e54d9f --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentDescriptorCollectionProvider.cs @@ -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 +{ + /// + /// A default implementation of + /// + public class DefaultViewComponentDescriptorCollectionProvider : IViewComponentDescriptorCollectionProvider + { + private readonly IViewComponentDescriptorProvider _descriptorProvider; + private ViewComponentDescriptorCollection _viewComponents; + + /// + /// Creates a new instance of . + /// + /// The . + public DefaultViewComponentDescriptorCollectionProvider(IViewComponentDescriptorProvider descriptorProvider) + { + _descriptorProvider = descriptorProvider; + } + + /// + 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); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentDescriptorProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentDescriptorProvider.cs new file mode 100644 index 0000000000..5653471212 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentDescriptorProvider.cs @@ -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 +{ + /// + /// Default implementation of . + /// + public class DefaultViewComponentDescriptorProvider : IViewComponentDescriptorProvider + { + private readonly IAssemblyProvider _assemblyProvider; + + /// + /// Creates a new . + /// + /// The . + public DefaultViewComponentDescriptorProvider(IAssemblyProvider assemblyProvider) + { + _assemblyProvider = assemblyProvider; + } + + /// + public virtual IEnumerable GetViewComponents() + { + var types = GetCandidateTypes(); + + return types + .Where(IsViewComponentType) + .Select(CreateCandidate); + } + + /// + /// Gets the candidate instances. The results of this will be provided to + /// for filtering. + /// + /// A list of instances. + protected virtual IEnumerable GetCandidateTypes() + { + var assemblies = _assemblyProvider.CandidateAssemblies; + return assemblies.SelectMany(a => a.ExportedTypes).Select(t => t.GetTypeInfo()); + } + + /// + /// Determines whether or not the given is a View Component class. + /// + /// The . + /// + /// true if represents a View Component class, otherwise false. + /// + 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; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentHelper.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentHelper.cs index 8e6a868296..05701f1b8b 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentHelper.cs @@ -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 InvokeAsync([NotNull] string name, params object[] args) - { - var componentType = SelectComponent(name); - return await InvokeAsync(componentType, args); - } - - public async Task 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 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 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); } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentInvoker.cs index bbbbed0fc6..bc57606117 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentInvoker.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentInvoker.cs @@ -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(_serviceProvider, _componentType.AsType()); - _viewComponentActivator.Activate(component, context); + var component = _typeActivatorCache.CreateInstance( + _serviceProvider, + context.ViewComponentDescriptor.Type); + _viewComponentActivator.Activate(component, context.ViewContext); return component; } private async Task 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) { diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentInvokerFactory.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentInvokerFactory.cs index 5bbaa6eb60..28ef96ab5c 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentInvokerFactory.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentInvokerFactory.cs @@ -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 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) + /// + // 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); } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentInvokerProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentInvokerProvider.cs deleted file mode 100644 index d0f2d16605..0000000000 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentInvokerProvider.cs +++ /dev/null @@ -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; } - } - - /// - public void OnProvidersExecuting([NotNull] ViewComponentInvokerProviderContext context) - { - context.Result = new DefaultViewComponentInvoker( - _serviceProvider, - _typeActivatorCache, - _viewComponentActivator, - context.ComponentType, - context.Arguments); - } - - /// - public void OnProvidersExecuted([NotNull] ViewComponentInvokerProviderContext context) - { - } - } -} diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentSelector.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentSelector.cs index 2765878b90..220d42320d 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentSelector.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentSelector.cs @@ -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 { + /// + /// Default implementation of . + /// public class DefaultViewComponentSelector : IViewComponentSelector { - private readonly IAssemblyProvider _assemblyProvider; + private readonly IViewComponentDescriptorCollectionProvider _descriptorProvider; - private ViewComponentCandidateCache _cache; + private ViewComponentDescriptorCache _cache; - public DefaultViewComponentSelector(IAssemblyProvider assemblyProvider) + /// + /// Creates a new . + /// + /// The . + public DefaultViewComponentSelector(IViewComponentDescriptorCollectionProvider descriptorProvider) { - _assemblyProvider = assemblyProvider; + _descriptorProvider = descriptorProvider; } - public Type SelectComponent([NotNull] string componentName) + /// + 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(); 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 _lookupByShortName; + private readonly ILookup _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 _lookupByShortName; - private readonly ILookup _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 candidates, string name) + private static ViewComponentDescriptor Select( + ILookup 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 { diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentActivator.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentActivator.cs index e103adeabb..74efb84ed7 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentActivator.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentActivator.cs @@ -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 { /// /// Provides methods to activate an instantiated ViewComponent diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentDescriptorCollectionProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentDescriptorCollectionProvider.cs new file mode 100644 index 0000000000..0287b3b964 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentDescriptorCollectionProvider.cs @@ -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 +{ + /// + /// Provides the currently cached collection of . + /// + /// + /// + /// 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 + /// in a thread safe way. + /// + /// + /// Default consumers of this service, are aware of the version and will recache + /// data as appropriate, but rely on the version being unique. + /// + /// + public interface IViewComponentDescriptorCollectionProvider + { + /// + /// Returns the current cached . + /// + ViewComponentDescriptorCollection ViewComponents { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentDescriptorProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentDescriptorProvider.cs new file mode 100644 index 0000000000..6c7a2b9a38 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentDescriptorProvider.cs @@ -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 +{ + /// + /// Discovers the View Components in the application. + /// + public interface IViewComponentDescriptorProvider + { + /// + /// Gets the set of . + /// + /// A list of . + IEnumerable GetViewComponents(); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentInvoker.cs index f7a2c60a11..f563f5abe1 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentInvoker.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentInvoker.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Microsoft.Framework.Internal; -namespace Microsoft.AspNet.Mvc +namespace Microsoft.AspNet.Mvc.ViewComponents { public interface IViewComponentInvoker { diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentInvokerFactory.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentInvokerFactory.cs index 12cb46aa28..d1139496bb 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentInvokerFactory.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentInvokerFactory.cs @@ -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); } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentInvokerProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentInvokerProvider.cs deleted file mode 100644 index 5580449f50..0000000000 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentInvokerProvider.cs +++ /dev/null @@ -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); - } -} diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentSelector.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentSelector.cs index 5e869a95ce..263b2de8d2 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentSelector.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentSelector.cs @@ -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 { + /// + /// Selects a View Component based on a View Component name. + /// public interface IViewComponentSelector { - Type SelectComponent([NotNull] string componentName); + /// + /// Selects a View Component based on . + /// + /// The View Component name. + /// A , or null if no match is found. + ViewComponentDescriptor SelectComponent([NotNull] string componentName); } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentContext.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentContext.cs index d067e02646..df9e41d4a1 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentContext.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentContext.cs @@ -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 { + /// + /// A context for View Components. + /// public class ViewComponentContext { - public ViewComponentContext([NotNull] TypeInfo componentType, [NotNull] ViewContext viewContext, + /// + /// Creates a new . + /// + /// + /// The for the View Component being invoked. + /// + /// The View Component arguments. + /// The . + /// The for writing output. + 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; } + /// + /// Gets the View Component arguments. + /// + public object[] Arguments { get; } - public ViewContext ViewContext { get; private set; } + /// + /// Gets the for the View Component being invoked. + /// + public ViewComponentDescriptor ViewComponentDescriptor { get; } - public TextWriter Writer { get; private set; } + /// + /// Gets the . + /// + public ViewContext ViewContext { get; } + + /// + /// Gets the for writing output. + /// + /// + /// or a similar overload is used to invoke the + /// View Component, then will be different than . + /// + public TextWriter Writer { get; } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentConventions.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentConventions.cs index 48481f3577..6b56a55e7b 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentConventions.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentConventions.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentDescriptor.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentDescriptor.cs new file mode 100644 index 0000000000..e0b3b00a89 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentDescriptor.cs @@ -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 +{ + /// + /// A descriptor for a View Component. + /// + public class ViewComponentDescriptor + { + /// + /// Gets or sets the full name. + /// + /// + /// + /// 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 + /// ViewComponent as a suffix, the suffix will be omitted from the . + /// + /// + /// Class Name: Contoso.Products.LoginViewComponent + /// View Component FullName: Contoso.Products.Login + /// + /// + /// Class Name: Contoso.Blog.Tags + /// View Component FullName: Contoso.Blog.Tags + /// + /// + /// If is used to set a name, then this will be used as + /// the . + /// + /// + /// [ViewComponent(Name = "Contoso.Forum.UsersOnline")] + /// public class OnlineUsersViewComponent + /// { + /// } + /// View Component FullName: Contoso.Forum.UsersOnline + /// + /// + public string FullName { get; set; } + + /// + /// Gets or sets the short name. + /// + /// + /// + /// The short name is defaulted to the name of the View Component class. If the View Component class uses + /// ViewComponent as a suffix, the suffix will be omitted from the . + /// + /// + /// Class Name: Contoso.Products.LoginViewComponent + /// View Component ShortName: Login + /// + /// + /// Class Name: Contoso.Blog.Tags + /// View Component ShortName: Tags + /// + /// + /// If is used to set a name, then the last segment of the + /// value (using '.' as a separate) will be used as the . + /// + /// + /// [ViewComponent(Name = "Contoso.Forum.UsersOnline")] + /// public class OnlineUsersViewComponent + /// { + /// } + /// View Component ShortName: UsersOnline + /// + /// + public string ShortName { get; set; } + + /// + /// Gets or sets the . + /// + public Type Type { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentDescriptorCollection.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentDescriptorCollection.cs new file mode 100644 index 0000000000..c45d396a8b --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentDescriptorCollection.cs @@ -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 +{ + /// + /// A cached collection of . + /// + public class ViewComponentDescriptorCollection + { + /// + /// Initializes a new instance of the . + /// + /// The result of view component discovery + /// The unique version of discovered view components. + public ViewComponentDescriptorCollection([NotNull] IEnumerable items, int version) + { + Items = new List(items); + Version = version; + } + + /// + /// Returns the cached . + /// + public IReadOnlyList Items { get; } + + /// + /// Returns the unique version of the currently cached items. + /// + public int Version { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentInvokerProviderContext.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentInvokerProviderContext.cs deleted file mode 100644 index cda03c4a49..0000000000 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentInvokerProviderContext.cs +++ /dev/null @@ -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; } - } -} diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentMethodSelector.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentMethodSelector.cs index ea4db1c18b..7ec0ab3906 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentMethodSelector.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewComponentMethodSelector.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs index 78f1f15c7a..104776165c 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs @@ -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"); } diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs index 02277883da..08f691871c 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs @@ -155,9 +155,11 @@ namespace Microsoft.AspNet.Mvc // These do caching so they should stay singleton services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + services.AddTransient(); services.AddTransient(); - services.AddTransient(); services.AddTransient(); // Security and Authorization diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ContentViewComponentResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ContentViewComponentResultTest.cs index 4c744801b8..fe0db9dda0 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ContentViewComponentResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ContentViewComponentResultTest.cs @@ -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; } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultViewComponentActivatorTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/DefaultViewComponentActivatorTests.cs similarity index 99% rename from test/Microsoft.AspNet.Mvc.Core.Test/DefaultViewComponentActivatorTests.cs rename to test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/DefaultViewComponentActivatorTests.cs index e315b59fd0..0bba6605ba 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultViewComponentActivatorTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/DefaultViewComponentActivatorTests.cs @@ -12,7 +12,7 @@ using Microsoft.AspNet.Routing; using Moq; using Xunit; -namespace Microsoft.AspNet.Mvc +namespace Microsoft.AspNet.Mvc.ViewComponents { public class DefaultViewComponentActivatorTests { diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/DefaultViewComponentDescriptorProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/DefaultViewComponentDescriptorProviderTest.cs new file mode 100644 index 0000000000..fa8a219952 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/DefaultViewComponentDescriptorProviderTest.cs @@ -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 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; + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/DefaultViewComponentSelectorTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/DefaultViewComponentSelectorTest.cs index 28bc0c96b0..d2bf140597 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/DefaultViewComponentSelectorTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/DefaultViewComponentSelectorTest.cs @@ -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 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())) + { + } + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/JsonViewComponentResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/JsonViewComponentResultTest.cs index bc507d8f76..f15d6c472d 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/JsonViewComponentResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/JsonViewComponentResultTest.cs @@ -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; } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewComponentConventionsTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewComponentConventionsTest.cs index 0f7d32e08b..e26bbb0065 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewComponentConventionsTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewComponentConventionsTest.cs @@ -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 { diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewViewComponentResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewViewComponentResultTest.cs index a0b15a2c0e..b0a9b94568 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewViewComponentResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewViewComponentResultTest.cs @@ -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; } } diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/DefaultOrderTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/DefaultOrderTest.cs index 52cff2b448..9beb3a0f19 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/DefaultOrderTest.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/DefaultOrderTest.cs @@ -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), null, -1000)] [InlineData(typeof(IConfigureOptions), null, -1000)] diff --git a/test/WebSites/TagHelpersWebSite/TagHelpers/TagCloudViewComponentTagHelper.cs b/test/WebSites/TagHelpersWebSite/TagHelpers/TagCloudViewComponentTagHelper.cs index df5683a401..bed887ffae 100644 --- a/test/WebSites/TagHelpersWebSite/TagHelpers/TagCloudViewComponentTagHelper.cs +++ b/test/WebSites/TagHelpersWebSite/TagHelpers/TagCloudViewComponentTagHelper.cs @@ -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());