[Fixes #4014] Add overload to AddControllerAsServices that uses the default controller discovery logic.
* Added ControllerFeature and ControllerFeatureProvider to perform controller discovery. * Changed controller discovery to use application parts. * Changed ControllerActionDescriptorProvider to make use of Application parts. * Simplified AddControllerAsServices to not accept any parameter and perform controller discovery through the ApplicationPartManager in the IMvcBuilder and IMvcCoreBuilder. Assemblies should be added to the ApplicationPartManager in order to discover controllers in them.
This commit is contained in:
parent
50af106b6a
commit
0d0aad41f5
|
|
@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
|||
/// <summary>
|
||||
/// An <see cref="ApplicationPart"/> backed by an <see cref="Assembly"/>.
|
||||
/// </summary>
|
||||
public class AssemblyPart : ApplicationPart
|
||||
public class AssemblyPart : ApplicationPart, IApplicationPartTypeProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initalizes a new <see cref="AssemblyPart"/> instance.
|
||||
|
|
@ -35,5 +35,8 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
|||
/// Gets the name of the <see cref="ApplicationPart"/>.
|
||||
/// </summary>
|
||||
public override string Name => Assembly.GetName().Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TypeInfo> Types => Assembly.DefinedTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes a set of types from an <see cref="ApplicationPart"/>.
|
||||
/// </summary>
|
||||
public interface IApplicationPartTypeProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the list of available types in the <see cref="ApplicationPart"/>.
|
||||
/// </summary>
|
||||
IEnumerable<TypeInfo> Types { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of controllers types in an MVC application. The <see cref="ControllerFeature"/> can be populated
|
||||
/// using the <see cref="ApplicationPartManager"/> that is available during startup at <see cref="IMvcBuilder.PartManager"/>
|
||||
/// and <see cref="IMvcCoreBuilder.PartManager"/> or at a later stage by requiring the <see cref="ApplicationPartManager"/>
|
||||
/// as a dependency in a component.
|
||||
/// </summary>
|
||||
public class ControllerFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the list of controller types in an MVC application.
|
||||
/// </summary>
|
||||
public IList<TypeInfo> Controllers { get; } = new List<TypeInfo>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Discovers controllers from a list of <see cref="ApplicationPart"/> instances.
|
||||
/// </summary>
|
||||
public class ControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
|
||||
{
|
||||
private const string ControllerTypeNameSuffix = "Controller";
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PopulateFeature(
|
||||
IEnumerable<ApplicationPart> parts,
|
||||
ControllerFeature feature)
|
||||
{
|
||||
foreach (var part in parts.OfType<IApplicationPartTypeProvider>())
|
||||
{
|
||||
foreach (var type in part.Types)
|
||||
{
|
||||
if (IsController(type) && !feature.Controllers.Contains(type))
|
||||
{
|
||||
feature.Controllers.Add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a given <paramref name="typeInfo"/> is a controller.
|
||||
/// </summary>
|
||||
/// <param name="typeInfo">The <see cref="TypeInfo"/> candidate.</param>
|
||||
/// <returns><code>true</code> if the type is a controller; otherwise <code>false</code>.</returns>
|
||||
protected virtual bool IsController(TypeInfo typeInfo)
|
||||
{
|
||||
if (!typeInfo.IsClass)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeInfo.IsAbstract)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We only consider public top-level classes as controllers. IsPublic returns false for nested
|
||||
// classes, regardless of visibility modifiers
|
||||
if (!typeInfo.IsPublic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeInfo.ContainsGenericParameters)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeInfo.IsDefined(typeof(NonControllerAttribute)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!typeInfo.Name.EndsWith(ControllerTypeNameSuffix, StringComparison.OrdinalIgnoreCase) &&
|
||||
!typeInfo.IsDefined(typeof(ControllerAttribute)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="IControllerTypeProvider"/> that identifies controller types from assemblies
|
||||
/// specified by the registered <see cref="IAssemblyProvider"/>.
|
||||
/// </summary>
|
||||
public class DefaultControllerTypeProvider : IControllerTypeProvider
|
||||
{
|
||||
private const string ControllerTypeName = "Controller";
|
||||
private static readonly TypeInfo ObjectTypeInfo = typeof(object).GetTypeInfo();
|
||||
private readonly IAssemblyProvider _assemblyProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="DefaultControllerTypeProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="assemblyProvider"><see cref="IAssemblyProvider"/> that provides assemblies to look for
|
||||
/// controllers in.</param>
|
||||
public DefaultControllerTypeProvider(IAssemblyProvider assemblyProvider)
|
||||
{
|
||||
_assemblyProvider = assemblyProvider;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<TypeInfo> ControllerTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
var candidateAssemblies = new HashSet<Assembly>(_assemblyProvider.CandidateAssemblies);
|
||||
var types = candidateAssemblies.SelectMany(a => a.DefinedTypes);
|
||||
return types.Where(typeInfo => IsController(typeInfo));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <c>true</c> if the <paramref name="typeInfo"/> is a controller. Otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
/// <param name="typeInfo">The <see cref="TypeInfo"/>.</param>
|
||||
/// <returns><c>true</c> if the <paramref name="typeInfo"/> is a controller. Otherwise <c>false</c>.</returns>
|
||||
protected internal virtual bool IsController(TypeInfo typeInfo)
|
||||
{
|
||||
if (typeInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(typeInfo));
|
||||
}
|
||||
|
||||
if (!typeInfo.IsClass)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeInfo.IsAbstract)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We only consider public top-level classes as controllers. IsPublic returns false for nested
|
||||
// classes, regardless of visibility modifiers
|
||||
if (!typeInfo.IsPublic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeInfo.ContainsGenericParameters)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeInfo.IsDefined(typeof(NonControllerAttribute)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!typeInfo.Name.EndsWith(ControllerTypeName, StringComparison.OrdinalIgnoreCase) &&
|
||||
!typeInfo.IsDefined(typeof(ControllerAttribute)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="IControllerTypeProvider"/> with a fixed set of types that are used as controllers.
|
||||
/// </summary>
|
||||
public class StaticControllerTypeProvider : IControllerTypeProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="StaticControllerTypeProvider"/>.
|
||||
/// </summary>
|
||||
public StaticControllerTypeProvider()
|
||||
: this(Enumerable.Empty<TypeInfo>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="StaticControllerTypeProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="controllerTypes">The sequence of controller <see cref="TypeInfo"/>.</param>
|
||||
public StaticControllerTypeProvider(IEnumerable<TypeInfo> controllerTypes)
|
||||
{
|
||||
if (controllerTypes == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(controllerTypes));
|
||||
}
|
||||
|
||||
ControllerTypes = new List<TypeInfo>(controllerTypes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of controller <see cref="TypeInfo"/>s.
|
||||
/// </summary>
|
||||
public IList<TypeInfo> ControllerTypes { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerable<TypeInfo> IControllerTypeProvider.ControllerTypes => ControllerTypes;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +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.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
|
|
@ -110,70 +110,22 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register the specified <paramref name="controllerTypes"/> as services and as a source for controller
|
||||
/// discovery.
|
||||
/// Registers discovered controllers as services in the <see cref="IServiceCollection"/>.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
|
||||
/// <param name="controllerTypes">A sequence of controller <see cref="Type"/>s to register.</param>
|
||||
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
|
||||
public static IMvcBuilder AddControllersAsServices(
|
||||
this IMvcBuilder builder,
|
||||
params Type[] controllerTypes)
|
||||
public static IMvcBuilder AddControllersAsServices(this IMvcBuilder builder)
|
||||
{
|
||||
return builder.AddControllersAsServices(controllerTypes.AsEnumerable());
|
||||
}
|
||||
var feature = new ControllerFeature();
|
||||
builder.PartManager.PopulateFeature(feature);
|
||||
|
||||
/// <summary>
|
||||
/// Register the specified <paramref name="controllerTypes"/> as services and as a source for controller
|
||||
/// discovery.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
|
||||
/// <param name="controllerTypes">A sequence of controller <see cref="Type"/>s to register.</param>
|
||||
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
|
||||
public static IMvcBuilder AddControllersAsServices(
|
||||
this IMvcBuilder builder,
|
||||
IEnumerable<Type> controllerTypes)
|
||||
foreach (var controller in feature.Controllers.Select(c => c.AsType()))
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
builder.Services.TryAddTransient(controller, controller);
|
||||
}
|
||||
|
||||
ControllersAsServices.AddControllersAsServices(builder.Services, controllerTypes);
|
||||
return builder;
|
||||
}
|
||||
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
|
||||
|
||||
/// <summary>
|
||||
/// Registers controller types from the specified <paramref name="controllerAssemblies"/> as services and as a source
|
||||
/// for controller discovery.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
|
||||
/// <param name="controllerAssemblies">Assemblies to scan.</param>
|
||||
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
|
||||
public static IMvcBuilder AddControllersAsServices(
|
||||
this IMvcBuilder builder,
|
||||
params Assembly[] controllerAssemblies)
|
||||
{
|
||||
return builder.AddControllersAsServices(controllerAssemblies.AsEnumerable());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers controller types from the specified <paramref name="controllerAssemblies"/> as services and as a source
|
||||
/// for controller discovery.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
|
||||
/// <param name="controllerAssemblies">Assemblies to scan.</param>
|
||||
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
|
||||
public static IMvcBuilder AddControllersAsServices(
|
||||
this IMvcBuilder builder,
|
||||
IEnumerable<Assembly> controllerAssemblies)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
ControllersAsServices.AddControllersAsServices(builder.Services, controllerAssemblies);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +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.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
|
@ -97,51 +97,23 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register the specified <paramref name="controllerTypes"/> as services and as a source for controller
|
||||
/// discovery.
|
||||
/// Registers discovered controllers as services in the <see cref="IServiceCollection"/>.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IMvcCoreBuilder"/>.</param>
|
||||
/// <param name="controllerTypes">A sequence of controller <see cref="Type"/>s to register.</param>
|
||||
/// <returns>The <see cref="IMvcCoreBuilder"/>.</returns>
|
||||
public static IMvcCoreBuilder AddControllersAsServices(
|
||||
this IMvcCoreBuilder builder,
|
||||
params Type[] controllerTypes)
|
||||
public static IMvcCoreBuilder AddControllersAsServices(this IMvcCoreBuilder builder)
|
||||
{
|
||||
return builder.AddControllersAsServices(controllerTypes.AsEnumerable());
|
||||
}
|
||||
var feature = new ControllerFeature();
|
||||
builder.PartManager.PopulateFeature(feature);
|
||||
|
||||
/// <summary>
|
||||
/// Register the specified <paramref name="controllerTypes"/> as services and as a source for controller
|
||||
/// discovery.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IMvcCoreBuilder"/>.</param>
|
||||
/// <param name="controllerTypes">A sequence of controller <see cref="Type"/>s to register.</param>
|
||||
/// <returns>The <see cref="IMvcCoreBuilder"/>.</returns>
|
||||
public static IMvcCoreBuilder AddControllersAsServices(
|
||||
this IMvcCoreBuilder builder,
|
||||
IEnumerable<Type> controllerTypes)
|
||||
{
|
||||
if (builder == null)
|
||||
foreach (var controller in feature.Controllers.Select(c => c.AsType()))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
builder.Services.TryAddTransient(controller, controller);
|
||||
}
|
||||
|
||||
ControllersAsServices.AddControllersAsServices(builder.Services, controllerTypes);
|
||||
return builder;
|
||||
}
|
||||
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
|
||||
|
||||
/// <summary>
|
||||
/// Registers controller types from the specified <paramref name="controllerAssemblies"/> as services and as a source
|
||||
/// for controller discovery.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IMvcCoreBuilder"/>.</param>
|
||||
/// <param name="controllerAssemblies">Assemblies to scan.</param>
|
||||
/// <returns>The <see cref="IMvcCoreBuilder"/>.</returns>
|
||||
public static IMvcCoreBuilder AddControllersAsServices(
|
||||
this IMvcCoreBuilder builder,
|
||||
params Assembly[] controllerAssemblies)
|
||||
{
|
||||
return builder.AddControllersAsServices(controllerAssemblies.AsEnumerable());
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -193,25 +165,5 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers controller types from the specified <paramref name="controllerAssemblies"/> as services and as a source
|
||||
/// for controller discovery.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IMvcCoreBuilder"/>.</param>
|
||||
/// <param name="controllerAssemblies">Assemblies to scan.</param>
|
||||
/// <returns>The <see cref="IMvcCoreBuilder"/>.</returns>
|
||||
public static IMvcCoreBuilder AddControllersAsServices(
|
||||
this IMvcCoreBuilder builder,
|
||||
IEnumerable<Assembly> controllerAssemblies)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
ControllersAsServices.AddControllersAsServices(builder.Services, controllerAssemblies);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ using Microsoft.AspNetCore.Mvc.Routing;
|
|||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.PlatformAbstractions;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
|
|
@ -45,6 +44,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
var partManager = GetApplicationPartManager(services);
|
||||
services.TryAddSingleton(partManager);
|
||||
|
||||
ConfigureDefaultFeatureProviders(partManager);
|
||||
ConfigureDefaultServices(services);
|
||||
AddMvcCoreServices(services);
|
||||
|
||||
|
|
@ -53,6 +53,14 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
return builder;
|
||||
}
|
||||
|
||||
private static void ConfigureDefaultFeatureProviders(ApplicationPartManager manager)
|
||||
{
|
||||
if (!manager.FeatureProviders.OfType<ControllerFeatureProvider>().Any())
|
||||
{
|
||||
manager.FeatureProviders.Add(new ControllerFeatureProvider());
|
||||
}
|
||||
}
|
||||
|
||||
private static ApplicationPartManager GetApplicationPartManager(IServiceCollection services)
|
||||
{
|
||||
var manager = GetServiceFromCollection<ApplicationPartManager>(services);
|
||||
|
|
@ -126,7 +134,6 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
// These are consumed only when creating action descriptors, then they can be de-allocated
|
||||
services.TryAddTransient<IAssemblyProvider, DefaultAssemblyProvider>();
|
||||
|
||||
services.TryAddTransient<IControllerTypeProvider, DefaultControllerTypeProvider>();
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Transient<IApplicationModelProvider, DefaultApplicationModelProvider>());
|
||||
services.TryAddEnumerable(
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
|
|
@ -13,18 +15,18 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
public class ControllerActionDescriptorProvider : IActionDescriptorProvider
|
||||
{
|
||||
private readonly ApplicationPartManager _partManager;
|
||||
private readonly IApplicationModelProvider[] _applicationModelProviders;
|
||||
private readonly IControllerTypeProvider _controllerTypeProvider;
|
||||
private readonly IEnumerable<IApplicationModelConvention> _conventions;
|
||||
|
||||
public ControllerActionDescriptorProvider(
|
||||
IControllerTypeProvider controllerTypeProvider,
|
||||
ApplicationPartManager partManager,
|
||||
IEnumerable<IApplicationModelProvider> applicationModelProviders,
|
||||
IOptions<MvcOptions> optionsAccessor)
|
||||
{
|
||||
if (controllerTypeProvider == null)
|
||||
if (partManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(controllerTypeProvider));
|
||||
throw new ArgumentNullException(nameof(partManager));
|
||||
}
|
||||
|
||||
if (applicationModelProviders == null)
|
||||
|
|
@ -37,7 +39,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
throw new ArgumentNullException(nameof(optionsAccessor));
|
||||
}
|
||||
|
||||
_controllerTypeProvider = controllerTypeProvider;
|
||||
_partManager = partManager;
|
||||
_applicationModelProviders = applicationModelProviders.OrderBy(p => p.Order).ToArray();
|
||||
_conventions = optionsAccessor.Value.Conventions;
|
||||
}
|
||||
|
|
@ -75,7 +77,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
internal protected ApplicationModel BuildModel()
|
||||
{
|
||||
var controllerTypes = _controllerTypeProvider.ControllerTypes;
|
||||
var controllerTypes = GetControllerTypes();
|
||||
var context = new ApplicationModelProviderContext(controllerTypes);
|
||||
|
||||
for (var i = 0; i < _applicationModelProviders.Length; i++)
|
||||
|
|
@ -90,5 +92,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
return context.Result;
|
||||
}
|
||||
|
||||
private IEnumerable<TypeInfo> GetControllerTypes()
|
||||
{
|
||||
var feature = new ControllerFeature();
|
||||
_partManager.PopulateFeature(feature);
|
||||
|
||||
return feature.Controllers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public static class ControllersAsServices
|
||||
{
|
||||
public static void AddControllersAsServices(IServiceCollection services, IEnumerable<Type> types)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (types == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(types));
|
||||
}
|
||||
|
||||
StaticControllerTypeProvider controllerTypeProvider = null;
|
||||
|
||||
controllerTypeProvider = services
|
||||
.Where(s => s.ServiceType == typeof(IControllerTypeProvider))
|
||||
.Select(s => s.ImplementationInstance)
|
||||
.OfType<StaticControllerTypeProvider>()
|
||||
.FirstOrDefault()
|
||||
?? new StaticControllerTypeProvider();
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
services.TryAddTransient(type, type);
|
||||
controllerTypeProvider.ControllerTypes.Add(type.GetTypeInfo());
|
||||
}
|
||||
|
||||
services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
|
||||
services.Replace(ServiceDescriptor.Singleton<IControllerTypeProvider>(controllerTypeProvider));
|
||||
}
|
||||
|
||||
public static void AddControllersAsServices(IServiceCollection services, IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (assemblies == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(assemblies));
|
||||
}
|
||||
|
||||
var assemblyProvider = new StaticAssemblyProvider();
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
assemblyProvider.CandidateAssemblies.Add(assembly);
|
||||
}
|
||||
|
||||
var controllerTypeProvider = new DefaultControllerTypeProvider(assemblyProvider);
|
||||
var controllerTypes = controllerTypeProvider.ControllerTypes;
|
||||
|
||||
AddControllersAsServices(services, controllerTypes.Select(type => type.AsType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,21 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
|||
Assert.Equal("Microsoft.AspNetCore.Mvc.Core.Test", name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AssemblyPart_Types_ReturnsDefinedTypes()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = typeof(AssemblyPartTest).GetTypeInfo().Assembly;
|
||||
var part = new AssemblyPart(assembly);
|
||||
|
||||
// Act
|
||||
var types = part.Types;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(assembly.DefinedTypes, types);
|
||||
Assert.NotSame(assembly.DefinedTypes, types);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AssemblyPart_Assembly_ReturnsAssembly()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,529 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.ControllerFeatureProviderControllers;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Controllers
|
||||
{
|
||||
public class ControllerFeatureProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void UserDefinedClass_DerivedFromController_IsController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(StoreController).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
var discovered = Assert.Single(feature.Controllers);
|
||||
Assert.Equal(controllerType, discovered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UserDefinedClass_DerivedFromControllerBase_IsController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(ProductsController).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
var discovered = Assert.Single(feature.Controllers);
|
||||
Assert.Equal(controllerType, discovered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UserDefinedClass_DerivedFromControllerBaseWithoutSuffix_IsController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(Products).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
var discovered = Assert.Single(feature.Controllers);
|
||||
Assert.Equal(controllerType, discovered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FrameworkControllerClass_IsNotController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(Controller).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.Controllers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FrameworkBaseControllerClass_IsNotController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(ControllerBase).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.Controllers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UserDefinedControllerClass_IsNotController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(ControllerFeatureProviderControllers.Controller).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.Controllers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Interface_IsNotController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(ITestController).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.Controllers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AbstractClass_IsNotController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(AbstractController).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.Controllers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DerivedAbstractClass_IsController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(DerivedAbstractController).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
var discovered = Assert.Single(feature.Controllers);
|
||||
Assert.Equal(controllerType, discovered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OpenGenericClass_IsNotController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(OpenGenericController<>).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.Controllers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithoutSuffixOrAncestorWithController_IsNotController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(NoSuffixPoco).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.Controllers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClosedGenericClass_IsController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(OpenGenericController<string>).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
var discovered = Assert.Single(feature.Controllers);
|
||||
Assert.Equal(controllerType, discovered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DerivedGenericClass_IsController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(DerivedGenericController).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
var discovered = Assert.Single(feature.Controllers);
|
||||
Assert.Equal(controllerType, discovered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Poco_WithNamingConvention_IsController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(PocoController).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
var discovered = Assert.Single(feature.Controllers);
|
||||
Assert.Equal(controllerType, discovered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoControllerSuffix_IsController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(NoSuffix).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
var discovered = Assert.Single(feature.Controllers);
|
||||
Assert.Equal(controllerType, discovered);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(DescendantLevel1))]
|
||||
[InlineData(typeof(DescendantLevel2))]
|
||||
public void AncestorTypeHasControllerAttribute_IsController(Type type)
|
||||
{
|
||||
// Arrange
|
||||
var manager = GetApplicationPartManager(type.GetTypeInfo());
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
var discovered = Assert.Single(feature.Controllers);
|
||||
Assert.Equal(type.GetTypeInfo(), discovered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AncestorTypeDoesNotHaveControllerAttribute_IsNotController()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(NoSuffixNoControllerAttribute).GetTypeInfo();
|
||||
var manager = GetApplicationPartManager(controllerType);
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.Controllers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFeature_OnlyRunsOnParts_ThatImplementIExportTypes()
|
||||
{
|
||||
// Arrange
|
||||
var otherPart = new Mock<ApplicationPart>();
|
||||
otherPart
|
||||
.As<IApplicationPartTypeProvider>()
|
||||
.Setup(t => t.Types)
|
||||
.Returns(new[] { typeof(PocoController).GetTypeInfo() });
|
||||
|
||||
var parts = new[] {
|
||||
Mock.Of<ApplicationPart>(),
|
||||
new TestApplicationPart(typeof(NoSuffix).GetTypeInfo()),
|
||||
otherPart.Object
|
||||
};
|
||||
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
var expected = new List<TypeInfo>
|
||||
{
|
||||
typeof(NoSuffix).GetTypeInfo(),
|
||||
typeof(PocoController).GetTypeInfo()
|
||||
};
|
||||
|
||||
var provider = new ControllerFeatureProvider();
|
||||
|
||||
// Act
|
||||
provider.PopulateFeature(parts, feature);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, feature.Controllers.ToList());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFeature_DoesNotAddDuplicates_ToTheListOfControllers()
|
||||
{
|
||||
// Arrange
|
||||
var otherPart = new Mock<ApplicationPart>();
|
||||
otherPart
|
||||
.As<IApplicationPartTypeProvider>()
|
||||
.Setup(t => t.Types)
|
||||
.Returns(new[] { typeof(PocoController).GetTypeInfo() });
|
||||
|
||||
var parts = new[] {
|
||||
Mock.Of<ApplicationPart>(),
|
||||
new TestApplicationPart(typeof(NoSuffix)),
|
||||
otherPart.Object
|
||||
};
|
||||
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
var expected = new List<TypeInfo>
|
||||
{
|
||||
typeof(NoSuffix).GetTypeInfo(),
|
||||
typeof(PocoController).GetTypeInfo()
|
||||
};
|
||||
|
||||
var provider = new ControllerFeatureProvider();
|
||||
|
||||
provider.PopulateFeature(parts, feature);
|
||||
|
||||
// Act
|
||||
provider.PopulateFeature(parts, feature);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, feature.Controllers.ToList());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(BaseNonControllerController))]
|
||||
[InlineData(typeof(BaseNonControllerControllerChild))]
|
||||
[InlineData(typeof(BasePocoNonControllerController))]
|
||||
[InlineData(typeof(BasePocoNonControllerControllerChild))]
|
||||
[InlineData(typeof(NonController))]
|
||||
[InlineData(typeof(NonControllerChild))]
|
||||
[InlineData(typeof(BaseNonControllerAttributeChildControllerControllerAttributeController))]
|
||||
[InlineData(typeof(PersonModel))] // Verifies that POCO type hierarchies that don't derive from controller return false.
|
||||
public void IsController_ReturnsFalse_IfTypeOrAncestorHasNonControllerAttribute(Type type)
|
||||
{
|
||||
// Arrange
|
||||
var manager = GetApplicationPartManager(type.GetTypeInfo());
|
||||
var feature = new ControllerFeature();
|
||||
|
||||
// Act
|
||||
manager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.Controllers);
|
||||
}
|
||||
|
||||
private static ApplicationPartManager GetApplicationPartManager(params TypeInfo[] types)
|
||||
{
|
||||
var manager = new ApplicationPartManager();
|
||||
manager.ApplicationParts.Add(new TestApplicationPart(types));
|
||||
manager.FeatureProviders.Add(new ControllerFeatureProvider());
|
||||
|
||||
return manager;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These controllers are used to test the ControllerFeatureProvider implementation
|
||||
// which REQUIRES that they be public top-level classes. To avoid having to stub out the
|
||||
// implementation of this class to test it, they are just top level classes. Don't reuse
|
||||
// these outside this test - find a better way or use nested classes to keep the tests
|
||||
// independent.
|
||||
namespace Microsoft.AspNetCore.Mvc.ControllerFeatureProviderControllers
|
||||
{
|
||||
public abstract class AbstractController : Controller
|
||||
{
|
||||
}
|
||||
|
||||
public class DerivedAbstractController : AbstractController
|
||||
{
|
||||
}
|
||||
|
||||
public class StoreController : Controller
|
||||
{
|
||||
}
|
||||
|
||||
public class ProductsController : ControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
public class Products : ControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[Controller]
|
||||
public abstract class Controller
|
||||
{
|
||||
}
|
||||
|
||||
public abstract class NoControllerAttributeBaseController
|
||||
{
|
||||
}
|
||||
|
||||
public class NoSuffixNoControllerAttribute : NoControllerAttributeBaseController
|
||||
{
|
||||
}
|
||||
|
||||
public class OpenGenericController<T> : Controller
|
||||
{
|
||||
}
|
||||
|
||||
public class DerivedGenericController : OpenGenericController<string>
|
||||
{
|
||||
}
|
||||
|
||||
public interface ITestController
|
||||
{
|
||||
}
|
||||
|
||||
public class NoSuffix : Controller
|
||||
{
|
||||
}
|
||||
|
||||
public class NoSuffixPoco
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class PocoController
|
||||
{
|
||||
}
|
||||
|
||||
[Controller]
|
||||
public class CustomBase
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Controller]
|
||||
public abstract class CustomAbstractBaseController
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class DescendantLevel1 : CustomBase
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class DescendantLevel2 : DescendantLevel1
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class AbstractChildWithoutSuffix : CustomAbstractBaseController
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[NonController]
|
||||
public class BasePocoNonControllerController
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Controller]
|
||||
public class BaseNonControllerAttributeChildControllerControllerAttributeController : BaseNonControllerController
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class BasePocoNonControllerControllerChild : BasePocoNonControllerController
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[NonController]
|
||||
public class BaseNonControllerController : Controller
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class BaseNonControllerControllerChild : BaseNonControllerController
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[NonController]
|
||||
public class NonControllerChild : Controller
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[NonController]
|
||||
public class NonController : Controller
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class DataModelBase
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EntityDataModel : DataModelBase
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class PersonModel : EntityDataModel
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,433 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.DefaultControllerTypeProviderControllers;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Controllers
|
||||
{
|
||||
public class DefaultControllerTypeProviderTest
|
||||
{
|
||||
private static readonly ISet<Assembly> CandidateAssemblies = new HashSet<Assembly>
|
||||
{
|
||||
typeof(StoreController).GetTypeInfo().Assembly
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void IsController_UserDefinedClass_DerivedFromController()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(StoreController).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.True(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_UserDefinedClass_DerivedFromControllerBase()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(ProductsController).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.True(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_UserDefinedClass_DerivedFromControllerBase_WithoutSuffix()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(Products).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.True(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_FrameworkControllerClass()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(Controller).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.False(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_FrameworkBaseControllerClass()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(Controller).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.False(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_UserDefinedControllerClass()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(DefaultControllerTypeProviderControllers.Controller).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.False(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_Interface()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(IController).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.False(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_AbstractClass()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(AbstractController).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.False(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_DerivedAbstractClass()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(DerivedAbstractController).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.True(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_OpenGenericClass()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(OpenGenericController<>).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.False(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_WithoutSuffixOrAncestorWithController()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(NoSuffixPoco).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.False(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_ClosedGenericClass()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(OpenGenericController<string>).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.True(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_DerivedGenericClass()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(DerivedGenericController).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.True(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_Poco_WithNamingConvention()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(PocoController).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.True(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_NoControllerSuffix()
|
||||
{
|
||||
// Arrange
|
||||
var typeInfo = typeof(NoSuffix).GetTypeInfo();
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.True(isController);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(DescendantLevel1))]
|
||||
[InlineData(typeof(DescendantLevel2))]
|
||||
public void IsController_ReturnsTrue_IfAncestorTypeHasControllerAttribute(Type type)
|
||||
{
|
||||
// Arrange
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(type.GetTypeInfo());
|
||||
|
||||
// Assert
|
||||
Assert.True(isController);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsController_ReturnsFalse_IfAncestorTypeDoesNotHaveControllerAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(typeof(NoSuffixNoControllerAttribute).GetTypeInfo());
|
||||
|
||||
// Assert
|
||||
Assert.False(isController);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(BaseNonControllerController))]
|
||||
[InlineData(typeof(BaseNonControllerControllerChild))]
|
||||
[InlineData(typeof(BasePocoNonControllerController))]
|
||||
[InlineData(typeof(BasePocoNonControllerControllerChild))]
|
||||
[InlineData(typeof(NonController))]
|
||||
[InlineData(typeof(NonControllerChild))]
|
||||
[InlineData(typeof(BaseNonControllerAttributeChildControllerControllerAttributeController))]
|
||||
[InlineData(typeof(PersonModel))] // Verifies that POCO type hierarchies that don't derive from controller return false.
|
||||
public void IsController_ReturnsFalse_IfTypeOrAncestorHasNonControllerAttribute(Type type)
|
||||
{
|
||||
// Arrange
|
||||
var provider = GetControllerTypeProvider();
|
||||
|
||||
// Act
|
||||
var isController = provider.IsController(type.GetTypeInfo());
|
||||
|
||||
// Assert
|
||||
Assert.False(isController);
|
||||
}
|
||||
|
||||
private static DefaultControllerTypeProvider GetControllerTypeProvider()
|
||||
{
|
||||
var assemblyProvider = new StaticAssemblyProvider();
|
||||
return new DefaultControllerTypeProvider(assemblyProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These controllers are used to test the DefaultControllerTypeProvider implementation
|
||||
// which REQUIRES that they be public top-level classes. To avoid having to stub out the
|
||||
// implementation of this class to test it, they are just top level classes. Don't reuse
|
||||
// these outside this test - find a better way or use nested classes to keep the tests
|
||||
// independent.
|
||||
namespace Microsoft.AspNetCore.Mvc.DefaultControllerTypeProviderControllers
|
||||
{
|
||||
public abstract class AbstractController : Controller
|
||||
{
|
||||
}
|
||||
|
||||
public class DerivedAbstractController : AbstractController
|
||||
{
|
||||
}
|
||||
|
||||
public class StoreController : Controller
|
||||
{
|
||||
}
|
||||
|
||||
public class ProductsController : ControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
public class Products : ControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
[Controller]
|
||||
public abstract class Controller
|
||||
{
|
||||
}
|
||||
|
||||
public abstract class NoControllerAttributeBaseController
|
||||
{
|
||||
}
|
||||
|
||||
public class NoSuffixNoControllerAttribute : NoControllerAttributeBaseController
|
||||
{
|
||||
}
|
||||
|
||||
public class OpenGenericController<T> : Controller
|
||||
{
|
||||
}
|
||||
|
||||
public class DerivedGenericController : OpenGenericController<string>
|
||||
{
|
||||
}
|
||||
|
||||
public interface IController
|
||||
{
|
||||
}
|
||||
|
||||
public class NoSuffix : Controller
|
||||
{
|
||||
}
|
||||
|
||||
public class NoSuffixPoco
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class PocoController
|
||||
{
|
||||
}
|
||||
|
||||
[Controller]
|
||||
public class CustomBase
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Controller]
|
||||
public abstract class CustomAbstractBaseController
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class DescendantLevel1 : CustomBase
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class DescendantLevel2 : DescendantLevel1
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class AbstractChildWithoutSuffix : CustomAbstractBaseController
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[NonController]
|
||||
public class BasePocoNonControllerController
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class BasePocoNonControllerControllerChild : BasePocoNonControllerController
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[NonController]
|
||||
public class BaseNonControllerController : Controller
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Controller]
|
||||
public class BaseNonControllerAttributeChildControllerControllerAttributeController : BaseNonControllerController
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class BaseNonControllerControllerChild : BaseNonControllerController
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[NonController]
|
||||
public class NonControllerChild : Controller
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[NonController]
|
||||
public class NonController : Controller
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class DataModelBase
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EntityDataModel : DataModelBase
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class PersonModel : EntityDataModel
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
|
|
@ -42,7 +43,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
Mock.Of<IServiceCollection>(),
|
||||
new ApplicationPartManager());
|
||||
|
||||
var part = new TestPart();
|
||||
var part = new TestApplicationPart();
|
||||
|
||||
// Act
|
||||
var result = builder.ConfigureApplicationPartManager(manager =>
|
||||
|
|
@ -59,22 +60,21 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
public void WithControllersAsServices_AddsTypesToControllerTypeProviderAndServiceCollection()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new Mock<IMvcBuilder>();
|
||||
var collection = new ServiceCollection();
|
||||
builder.SetupGet(b => b.Services).Returns(collection);
|
||||
|
||||
var controllerTypes = new[]
|
||||
{
|
||||
typeof(ControllerTypeA),
|
||||
typeof(TypeBController),
|
||||
};
|
||||
}.Select(t => t.GetTypeInfo()).ToArray();
|
||||
|
||||
var builder = new MvcBuilder(collection, GetApplicationPartManager(controllerTypes));
|
||||
|
||||
// Act
|
||||
builder.Object.AddControllersAsServices(controllerTypes);
|
||||
builder.AddControllersAsServices();
|
||||
|
||||
// Assert
|
||||
var services = collection.ToList();
|
||||
Assert.Equal(4, services.Count);
|
||||
Assert.Equal(3, services.Count);
|
||||
Assert.Equal(typeof(ControllerTypeA), services[0].ServiceType);
|
||||
Assert.Equal(typeof(ControllerTypeA), services[0].ImplementationType);
|
||||
Assert.Equal(ServiceLifetime.Transient, services[0].Lifetime);
|
||||
|
|
@ -86,16 +86,45 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
Assert.Equal(typeof(IControllerActivator), services[2].ServiceType);
|
||||
Assert.Equal(typeof(ServiceBasedControllerActivator), services[2].ImplementationType);
|
||||
Assert.Equal(ServiceLifetime.Transient, services[2].Lifetime);
|
||||
|
||||
Assert.Equal(typeof(IControllerTypeProvider), services[3].ServiceType);
|
||||
var typeProvider = Assert.IsType<StaticControllerTypeProvider>(services[3].ImplementationInstance);
|
||||
Assert.Equal(controllerTypes, typeProvider.ControllerTypes.OrderBy(c => c.Name).Select(t => t.AsType()));
|
||||
Assert.Equal(ServiceLifetime.Singleton, services[3].Lifetime);
|
||||
}
|
||||
|
||||
private class TestPart : ApplicationPart
|
||||
[Fact]
|
||||
public void AddControllerAsServices_MultipleCalls_RetainsPreviouslyAddedTypes()
|
||||
{
|
||||
public override string Name => "Test";
|
||||
// Arrange
|
||||
var services = new ServiceCollection();
|
||||
var manager = new ApplicationPartManager();
|
||||
manager.ApplicationParts.Add(new TestApplicationPart(typeof(ControllerOne), typeof(ControllerTwo)));
|
||||
manager.FeatureProviders.Add(new TestFeatureProvider());
|
||||
var builder = new MvcBuilder(services, manager);
|
||||
|
||||
builder.AddControllersAsServices();
|
||||
|
||||
// Act
|
||||
builder.AddControllersAsServices();
|
||||
|
||||
// Assert 2
|
||||
var collection = services.ToList();
|
||||
Assert.Equal(3, collection.Count);
|
||||
Assert.Single(collection, d => d.ServiceType.Equals(typeof(ControllerOne)));
|
||||
Assert.Single(collection, d => d.ServiceType.Equals(typeof(ControllerTwo)));
|
||||
}
|
||||
|
||||
private class ControllerOne
|
||||
{
|
||||
}
|
||||
|
||||
private class ControllerTwo
|
||||
{
|
||||
}
|
||||
|
||||
private static ApplicationPartManager GetApplicationPartManager(params TypeInfo[] types)
|
||||
{
|
||||
var manager = new ApplicationPartManager();
|
||||
manager.ApplicationParts.Add(new TestApplicationPart(types));
|
||||
manager.FeatureProviders.Add(new ControllerFeatureProvider());
|
||||
|
||||
return manager;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Mvc.DependencyInjection
|
|||
Mock.Of<IServiceCollection>(),
|
||||
new ApplicationPartManager());
|
||||
|
||||
var part = new TestPart();
|
||||
var part = new TestApplicationPart();
|
||||
|
||||
// Act
|
||||
var result = builder.ConfigureApplicationPartManager(manager =>
|
||||
|
|
@ -51,10 +51,5 @@ namespace Microsoft.AspNetCore.Mvc.DependencyInjection
|
|||
Assert.Same(result, builder);
|
||||
Assert.Equal(new ApplicationPart[] { part }, builder.PartManager.ApplicationParts.ToArray());
|
||||
}
|
||||
|
||||
private class TestPart : ApplicationPart
|
||||
{
|
||||
public override string Name => "Test";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ using System.Reflection;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
|
|
@ -543,17 +545,26 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
|||
|
||||
var options = new TestOptionsManager<MvcOptions>();
|
||||
|
||||
var controllerTypeProvider = new StaticControllerTypeProvider(controllerTypes);
|
||||
var manager = GetApplicationManager(controllerTypes);
|
||||
|
||||
var modelProvider = new DefaultApplicationModelProvider(options);
|
||||
|
||||
var provider = new ControllerActionDescriptorProvider(
|
||||
controllerTypeProvider,
|
||||
manager,
|
||||
new[] { modelProvider },
|
||||
options);
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
private static ApplicationPartManager GetApplicationManager(List<TypeInfo> controllerTypes)
|
||||
{
|
||||
var manager = new ApplicationPartManager();
|
||||
manager.ApplicationParts.Add(new TestApplicationPart(controllerTypes));
|
||||
manager.FeatureProviders.Add(new TestFeatureProvider());
|
||||
return manager;
|
||||
}
|
||||
|
||||
private static HttpContext GetHttpContext(string httpMethod)
|
||||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc.Abstractions;
|
|||
using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
|
@ -1449,11 +1450,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
}
|
||||
|
||||
var controllerTypeProvider = new StaticControllerTypeProvider(new[] { controllerTypeInfo });
|
||||
var manager = GetApplicationManager(new[] { controllerTypeInfo });
|
||||
|
||||
var modelProvider = new DefaultApplicationModelProvider(options);
|
||||
|
||||
var provider = new ControllerActionDescriptorProvider(
|
||||
controllerTypeProvider,
|
||||
manager,
|
||||
new[] { modelProvider },
|
||||
options);
|
||||
|
||||
|
|
@ -1465,11 +1467,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
var options = new TestOptionsManager<MvcOptions>();
|
||||
|
||||
var controllerTypeProvider = new StaticControllerTypeProvider(controllerTypeInfos);
|
||||
var manager = GetApplicationManager(controllerTypeInfos);
|
||||
var modelProvider = new DefaultApplicationModelProvider(options);
|
||||
|
||||
var provider = new ControllerActionDescriptorProvider(
|
||||
controllerTypeProvider,
|
||||
manager,
|
||||
new[] { modelProvider },
|
||||
options);
|
||||
|
||||
|
|
@ -1483,17 +1485,26 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var options = new TestOptionsManager<MvcOptions>();
|
||||
options.Value.Conventions.Add(convention);
|
||||
|
||||
var controllerTypeProvider = new StaticControllerTypeProvider(new[] { controllerTypeInfo });
|
||||
var manager = GetApplicationManager(new[] { controllerTypeInfo });
|
||||
|
||||
var modelProvider = new DefaultApplicationModelProvider(options);
|
||||
|
||||
var provider = new ControllerActionDescriptorProvider(
|
||||
controllerTypeProvider,
|
||||
manager,
|
||||
new[] { modelProvider },
|
||||
options);
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
private static ApplicationPartManager GetApplicationManager(IEnumerable<TypeInfo> controllerTypes)
|
||||
{
|
||||
var manager = new ApplicationPartManager();
|
||||
manager.ApplicationParts.Add(new TestApplicationPart(controllerTypes));
|
||||
manager.FeatureProviders.Add(new TestFeatureProvider());
|
||||
return manager;
|
||||
}
|
||||
|
||||
private IEnumerable<ActionDescriptor> GetDescriptors(params TypeInfo[] controllerTypeInfos)
|
||||
{
|
||||
var provider = GetProvider(controllerTypeInfos);
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class ControllerAsServicesTest
|
||||
{
|
||||
[Fact]
|
||||
public void AddControllerAsServices_MultipleCalls_RetainsPreviouslyAddedTypes()
|
||||
{
|
||||
// Arrange
|
||||
var services = new ServiceCollection();
|
||||
|
||||
// Act 1
|
||||
ControllersAsServices.AddControllersAsServices(services, new Type[] { typeof(ControllerOne) });
|
||||
|
||||
// Assert 1
|
||||
var serviceDescriptor = Assert.Single(services, s => s.ServiceType == typeof(IControllerTypeProvider));
|
||||
var controllerTypeProvider = Assert.IsType<StaticControllerTypeProvider>(serviceDescriptor.ImplementationInstance);
|
||||
var expectedControllerType = Assert.Single(controllerTypeProvider.ControllerTypes);
|
||||
|
||||
// Act 2
|
||||
ControllersAsServices.AddControllersAsServices(services, new Type[] { typeof(ControllerTwo) });
|
||||
|
||||
// Assert 2
|
||||
serviceDescriptor = Assert.Single(services, s => s.ServiceType == typeof(IControllerTypeProvider));
|
||||
controllerTypeProvider = Assert.IsType<StaticControllerTypeProvider>(serviceDescriptor.ImplementationInstance);
|
||||
Assert.Equal(2, controllerTypeProvider.ControllerTypes.Count);
|
||||
Assert.Same(expectedControllerType, controllerTypeProvider.ControllerTypes[0]);
|
||||
Assert.Same(typeof(ControllerTwo), controllerTypeProvider.ControllerTypes[1]);
|
||||
}
|
||||
|
||||
private class ControllerOne
|
||||
{
|
||||
}
|
||||
|
||||
private class ControllerTwo
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
public class TestApplicationPart : ApplicationPart, IApplicationPartTypeProvider
|
||||
{
|
||||
public TestApplicationPart()
|
||||
{
|
||||
Types = Enumerable.Empty<TypeInfo>();
|
||||
}
|
||||
|
||||
public TestApplicationPart(params TypeInfo[] types)
|
||||
{
|
||||
Types = types;
|
||||
}
|
||||
|
||||
public TestApplicationPart(IEnumerable<TypeInfo> types)
|
||||
{
|
||||
Types = types;
|
||||
}
|
||||
|
||||
public TestApplicationPart(IEnumerable<Type> types)
|
||||
:this(types.Select(t => t.GetTypeInfo()))
|
||||
{
|
||||
}
|
||||
|
||||
public TestApplicationPart(params Type[] types)
|
||||
: this(types.Select(t => t.GetTypeInfo()))
|
||||
{
|
||||
}
|
||||
|
||||
public override string Name => "Test part";
|
||||
|
||||
public IEnumerable<TypeInfo> Types { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
public class TestFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
|
||||
{
|
||||
private readonly Func<TypeInfo, bool> _filter;
|
||||
|
||||
public TestFeatureProvider()
|
||||
: this(t => true)
|
||||
{
|
||||
}
|
||||
|
||||
public TestFeatureProvider(Func<TypeInfo, bool> filter)
|
||||
{
|
||||
_filter = filter;
|
||||
}
|
||||
|
||||
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
|
||||
{
|
||||
foreach (var type in parts.OfType<IApplicationPartTypeProvider>().SelectMany(t => t.Types).Where(_filter))
|
||||
{
|
||||
feature.Controllers.Add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,8 +10,6 @@ using Microsoft.AspNetCore.Hosting;
|
|||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -74,6 +72,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
|
||||
var manager = new ApplicationPartManager();
|
||||
manager.ApplicationParts.Add(new AssemblyPart(startupAssembly));
|
||||
manager.FeatureProviders.Add(new ControllerFeatureProvider());
|
||||
services.AddSingleton(manager);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
|
|
@ -371,9 +372,7 @@ namespace System.Web.Http
|
|||
|
||||
private ControllerActionDescriptorProvider CreateProvider()
|
||||
{
|
||||
var assemblyProvider = new StaticAssemblyProvider();
|
||||
assemblyProvider.CandidateAssemblies.Add(GetType().GetTypeInfo().Assembly);
|
||||
var controllerTypeProvider = new NamespaceFilteredControllerTypeProvider(assemblyProvider);
|
||||
var manager = GetApplicationManager(GetType().GetTypeInfo().Assembly.DefinedTypes.ToArray());
|
||||
|
||||
var options = new MvcOptions();
|
||||
|
||||
|
|
@ -393,7 +392,7 @@ namespace System.Web.Http
|
|||
var modelProvider = new DefaultApplicationModelProvider(optionsAccessor.Object);
|
||||
|
||||
var provider = new ControllerActionDescriptorProvider(
|
||||
controllerTypeProvider,
|
||||
manager,
|
||||
new[] { modelProvider },
|
||||
optionsAccessor.Object);
|
||||
|
||||
|
|
@ -406,20 +405,49 @@ namespace System.Web.Http
|
|||
provider.OnProvidersExecuted(context);
|
||||
}
|
||||
|
||||
private class NamespaceFilteredControllerTypeProvider : DefaultControllerTypeProvider
|
||||
private static ApplicationPartManager GetApplicationManager(params TypeInfo[] controllerTypes)
|
||||
{
|
||||
public NamespaceFilteredControllerTypeProvider(IAssemblyProvider provider)
|
||||
: base(provider)
|
||||
{
|
||||
var manager = new ApplicationPartManager();
|
||||
manager.ApplicationParts.Add(new TestPart(controllerTypes));
|
||||
manager.FeatureProviders.Add(new TestProvider());
|
||||
manager.FeatureProviders.Add(new NamespaceFilteredControllersFeatureProvider());
|
||||
return manager;
|
||||
}
|
||||
|
||||
private class TestPart : ApplicationPart, IApplicationPartTypeProvider
|
||||
{
|
||||
public TestPart(IEnumerable<TypeInfo> types)
|
||||
{
|
||||
Types = types;
|
||||
}
|
||||
|
||||
public override IEnumerable<TypeInfo> ControllerTypes
|
||||
public override string Name => "Test";
|
||||
|
||||
public IEnumerable<TypeInfo> Types { get; }
|
||||
}
|
||||
|
||||
private class TestProvider : IApplicationFeatureProvider<ControllerFeature>
|
||||
{
|
||||
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
|
||||
{
|
||||
get
|
||||
foreach (var type in parts.OfType<IApplicationPartTypeProvider>().SelectMany(t => t.Types))
|
||||
{
|
||||
return base.ControllerTypes
|
||||
.Where(typeInfo => typeInfo.Namespace == "System.Web.Http.TestControllers");
|
||||
feature.Controllers.Add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class NamespaceFilteredControllersFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
|
||||
{
|
||||
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
|
||||
{
|
||||
var controllers = feature.Controllers.ToList();
|
||||
foreach (var controller in controllers)
|
||||
{
|
||||
if (controller.Namespace != "System.Web.Http.TestControllers")
|
||||
{
|
||||
feature.Controllers.Remove(controller);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using ControllersFromServicesClassLibrary;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace ControllersFromServicesWebSite
|
||||
|
|
@ -14,18 +18,29 @@ namespace ControllersFromServicesWebSite
|
|||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services
|
||||
var builder = services
|
||||
.AddMvc()
|
||||
.AddControllersAsServices(typeof(AnotherController))
|
||||
.AddControllersAsServices(new[]
|
||||
{
|
||||
typeof(TimeScheduleController).GetTypeInfo().Assembly
|
||||
});
|
||||
.ConfigureApplicationPartManager(manager => manager.ApplicationParts.Clear())
|
||||
.AddApplicationPart(typeof(TimeScheduleController).GetTypeInfo().Assembly)
|
||||
.ConfigureApplicationPartManager(manager => manager.ApplicationParts.Add(new TypesPart(typeof(AnotherController))))
|
||||
.AddControllersAsServices();
|
||||
|
||||
services.AddTransient<QueryValueService>();
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
}
|
||||
|
||||
private class TypesPart : ApplicationPart, IApplicationPartTypeProvider
|
||||
{
|
||||
public TypesPart(params Type[] types)
|
||||
{
|
||||
Types = types.Select(t => t.GetTypeInfo());
|
||||
}
|
||||
|
||||
public override string Name => string.Join(", ", Types.Select(t => t.FullName));
|
||||
|
||||
public IEnumerable<TypeInfo> Types { get; }
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
app.UseCultureReplacer();
|
||||
|
|
|
|||
Loading…
Reference in New Issue