diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/AssemblyPart.cs b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/AssemblyPart.cs
index 0147f59eb7..61ac71bb01 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/AssemblyPart.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/AssemblyPart.cs
@@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
///
/// An backed by an .
///
- public class AssemblyPart : ApplicationPart
+ public class AssemblyPart : ApplicationPart, IApplicationPartTypeProvider
{
///
/// Initalizes a new instance.
@@ -35,5 +35,8 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
/// Gets the name of the .
///
public override string Name => Assembly.GetName().Name;
+
+ ///
+ public IEnumerable Types => Assembly.DefinedTypes;
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/IApplicationPartTypeProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/IApplicationPartTypeProvider.cs
new file mode 100644
index 0000000000..616c9a9aea
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/IApplicationPartTypeProvider.cs
@@ -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
+{
+ ///
+ /// Exposes a set of types from an .
+ ///
+ public interface IApplicationPartTypeProvider
+ {
+ ///
+ /// Gets the list of available types in the .
+ ///
+ IEnumerable Types { get; }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Controllers/ControllerFeature.cs b/src/Microsoft.AspNetCore.Mvc.Core/Controllers/ControllerFeature.cs
new file mode 100644
index 0000000000..e3c8eaafc5
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Controllers/ControllerFeature.cs
@@ -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
+{
+ ///
+ /// The list of controllers types in an MVC application. The can be populated
+ /// using the that is available during startup at
+ /// and or at a later stage by requiring the
+ /// as a dependency in a component.
+ ///
+ public class ControllerFeature
+ {
+ ///
+ /// Gets the list of controller types in an MVC application.
+ ///
+ public IList Controllers { get; } = new List();
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Controllers/ControllerFeatureProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/Controllers/ControllerFeatureProvider.cs
new file mode 100644
index 0000000000..6b5a3616d2
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Controllers/ControllerFeatureProvider.cs
@@ -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
+{
+ ///
+ /// Discovers controllers from a list of instances.
+ ///
+ public class ControllerFeatureProvider : IApplicationFeatureProvider
+ {
+ private const string ControllerTypeNameSuffix = "Controller";
+
+ ///
+ public void PopulateFeature(
+ IEnumerable parts,
+ ControllerFeature feature)
+ {
+ foreach (var part in parts.OfType())
+ {
+ foreach (var type in part.Types)
+ {
+ if (IsController(type) && !feature.Controllers.Contains(type))
+ {
+ feature.Controllers.Add(type);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Determines if a given is a controller.
+ ///
+ /// The candidate.
+ /// true if the type is a controller; otherwise false.
+ 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;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Controllers/DefaultControllerTypeProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/Controllers/DefaultControllerTypeProvider.cs
deleted file mode 100644
index a6d93d60c6..0000000000
--- a/src/Microsoft.AspNetCore.Mvc.Core/Controllers/DefaultControllerTypeProvider.cs
+++ /dev/null
@@ -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
-{
- ///
- /// A that identifies controller types from assemblies
- /// specified by the registered .
- ///
- public class DefaultControllerTypeProvider : IControllerTypeProvider
- {
- private const string ControllerTypeName = "Controller";
- private static readonly TypeInfo ObjectTypeInfo = typeof(object).GetTypeInfo();
- private readonly IAssemblyProvider _assemblyProvider;
-
- ///
- /// Initializes a new instance of .
- ///
- /// that provides assemblies to look for
- /// controllers in.
- public DefaultControllerTypeProvider(IAssemblyProvider assemblyProvider)
- {
- _assemblyProvider = assemblyProvider;
- }
-
- ///
- public virtual IEnumerable ControllerTypes
- {
- get
- {
- var candidateAssemblies = new HashSet(_assemblyProvider.CandidateAssemblies);
- var types = candidateAssemblies.SelectMany(a => a.DefinedTypes);
- return types.Where(typeInfo => IsController(typeInfo));
- }
- }
-
- ///
- /// Returns true if the is a controller. Otherwise false.
- ///
- /// The .
- /// true if the is a controller. Otherwise false.
- 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;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Controllers/StaticControllerTypeProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/Controllers/StaticControllerTypeProvider.cs
deleted file mode 100644
index a20c7fce1d..0000000000
--- a/src/Microsoft.AspNetCore.Mvc.Core/Controllers/StaticControllerTypeProvider.cs
+++ /dev/null
@@ -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
-{
- ///
- /// A with a fixed set of types that are used as controllers.
- ///
- public class StaticControllerTypeProvider : IControllerTypeProvider
- {
- ///
- /// Initializes a new instance of .
- ///
- public StaticControllerTypeProvider()
- : this(Enumerable.Empty())
- {
- }
-
- ///
- /// Initializes a new instance of .
- ///
- /// The sequence of controller .
- public StaticControllerTypeProvider(IEnumerable controllerTypes)
- {
- if (controllerTypes == null)
- {
- throw new ArgumentNullException(nameof(controllerTypes));
- }
-
- ControllerTypes = new List(controllerTypes);
- }
-
- ///
- /// Gets the list of controller s.
- ///
- public IList ControllerTypes { get; }
-
- ///
- IEnumerable IControllerTypeProvider.ControllerTypes => ControllerTypes;
- }
-}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreMvcBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreMvcBuilderExtensions.cs
index 4c57152edc..9603a89083 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreMvcBuilderExtensions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreMvcBuilderExtensions.cs
@@ -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
}
///
- /// Register the specified as services and as a source for controller
- /// discovery.
+ /// Registers discovered controllers as services in the .
///
/// The .
- /// A sequence of controller s to register.
/// The .
- 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);
- ///
- /// Register the specified as services and as a source for controller
- /// discovery.
- ///
- /// The .
- /// A sequence of controller s to register.
- /// The .
- public static IMvcBuilder AddControllersAsServices(
- this IMvcBuilder builder,
- IEnumerable 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());
- ///
- /// Registers controller types from the specified as services and as a source
- /// for controller discovery.
- ///
- /// The .
- /// Assemblies to scan.
- /// The .
- public static IMvcBuilder AddControllersAsServices(
- this IMvcBuilder builder,
- params Assembly[] controllerAssemblies)
- {
- return builder.AddControllersAsServices(controllerAssemblies.AsEnumerable());
- }
-
- ///
- /// Registers controller types from the specified as services and as a source
- /// for controller discovery.
- ///
- /// The .
- /// Assemblies to scan.
- /// The .
- public static IMvcBuilder AddControllersAsServices(
- this IMvcBuilder builder,
- IEnumerable controllerAssemblies)
- {
- if (builder == null)
- {
- throw new ArgumentNullException(nameof(builder));
- }
-
- ControllersAsServices.AddControllersAsServices(builder.Services, controllerAssemblies);
return builder;
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreMvcCoreBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreMvcCoreBuilderExtensions.cs
index 3ef94e1343..f550810bbb 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreMvcCoreBuilderExtensions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreMvcCoreBuilderExtensions.cs
@@ -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
}
///
- /// Register the specified as services and as a source for controller
- /// discovery.
+ /// Registers discovered controllers as services in the .
///
/// The .
- /// A sequence of controller s to register.
/// The .
- 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);
- ///
- /// Register the specified as services and as a source for controller
- /// discovery.
- ///
- /// The .
- /// A sequence of controller s to register.
- /// The .
- public static IMvcCoreBuilder AddControllersAsServices(
- this IMvcCoreBuilder builder,
- IEnumerable 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());
- ///
- /// Registers controller types from the specified as services and as a source
- /// for controller discovery.
- ///
- /// The .
- /// Assemblies to scan.
- /// The .
- public static IMvcCoreBuilder AddControllersAsServices(
- this IMvcCoreBuilder builder,
- params Assembly[] controllerAssemblies)
- {
- return builder.AddControllersAsServices(controllerAssemblies.AsEnumerable());
+ return builder;
}
///
@@ -193,25 +165,5 @@ namespace Microsoft.Extensions.DependencyInjection
return builder;
}
-
- ///
- /// Registers controller types from the specified as services and as a source
- /// for controller discovery.
- ///
- /// The .
- /// Assemblies to scan.
- /// The .
- public static IMvcCoreBuilder AddControllersAsServices(
- this IMvcCoreBuilder builder,
- IEnumerable controllerAssemblies)
- {
- if (builder == null)
- {
- throw new ArgumentNullException(nameof(builder));
- }
-
- ControllersAsServices.AddControllersAsServices(builder.Services, controllerAssemblies);
- return builder;
- }
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreServiceCollectionExtensions.cs
index 6c21cf5c65..01e6b741ea 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreServiceCollectionExtensions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreServiceCollectionExtensions.cs
@@ -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().Any())
+ {
+ manager.FeatureProviders.Add(new ControllerFeatureProvider());
+ }
+ }
+
private static ApplicationPartManager GetApplicationPartManager(IServiceCollection services)
{
var manager = GetServiceFromCollection(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();
- services.TryAddTransient();
services.TryAddEnumerable(
ServiceDescriptor.Transient());
services.TryAddEnumerable(
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorProvider.cs
index d2df6a11ff..f6706effe1 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorProvider.cs
@@ -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 _conventions;
public ControllerActionDescriptorProvider(
- IControllerTypeProvider controllerTypeProvider,
+ ApplicationPartManager partManager,
IEnumerable applicationModelProviders,
IOptions 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 GetControllerTypes()
+ {
+ var feature = new ControllerFeature();
+ _partManager.PopulateFeature(feature);
+
+ return feature.Controllers;
+ }
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllersAsServices.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllersAsServices.cs
deleted file mode 100644
index a6b3985fa1..0000000000
--- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllersAsServices.cs
+++ /dev/null
@@ -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 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()
- .FirstOrDefault()
- ?? new StaticControllerTypeProvider();
-
- foreach (var type in types)
- {
- services.TryAddTransient(type, type);
- controllerTypeProvider.ControllerTypes.Add(type.GetTypeInfo());
- }
-
- services.Replace(ServiceDescriptor.Transient());
- services.Replace(ServiceDescriptor.Singleton(controllerTypeProvider));
- }
-
- public static void AddControllersAsServices(IServiceCollection services, IEnumerable 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()));
- }
- }
-}
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationParts/AssemblyPartTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationParts/AssemblyPartTest.cs
index 591026fa5d..dea3859282 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationParts/AssemblyPartTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationParts/AssemblyPartTest.cs
@@ -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()
{
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Controllers/ControllerFeatureProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Controllers/ControllerFeatureProviderTest.cs
new file mode 100644
index 0000000000..69bd65a80e
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Controllers/ControllerFeatureProviderTest.cs
@@ -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).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();
+ otherPart
+ .As()
+ .Setup(t => t.Types)
+ .Returns(new[] { typeof(PocoController).GetTypeInfo() });
+
+ var parts = new[] {
+ Mock.Of(),
+ new TestApplicationPart(typeof(NoSuffix).GetTypeInfo()),
+ otherPart.Object
+ };
+
+ var feature = new ControllerFeature();
+
+ var expected = new List
+ {
+ 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();
+ otherPart
+ .As()
+ .Setup(t => t.Types)
+ .Returns(new[] { typeof(PocoController).GetTypeInfo() });
+
+ var parts = new[] {
+ Mock.Of(),
+ new TestApplicationPart(typeof(NoSuffix)),
+ otherPart.Object
+ };
+
+ var feature = new ControllerFeature();
+
+ var expected = new List
+ {
+ 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 : Controller
+ {
+ }
+
+ public class DerivedGenericController : OpenGenericController
+ {
+ }
+
+ 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
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Controllers/DefaultControllerTypeProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Controllers/DefaultControllerTypeProviderTest.cs
deleted file mode 100644
index 0a0df2d162..0000000000
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Controllers/DefaultControllerTypeProviderTest.cs
+++ /dev/null
@@ -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 CandidateAssemblies = new HashSet
- {
- 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).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 : Controller
- {
- }
-
- public class DerivedGenericController : OpenGenericController
- {
- }
-
- 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
- {
-
- }
-}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/DependencyInjection/MvcBuilderExtensionsTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/DependencyInjection/MvcBuilderExtensionsTest.cs
index 853330b643..1aa3240f6f 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/DependencyInjection/MvcBuilderExtensionsTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/DependencyInjection/MvcBuilderExtensionsTest.cs
@@ -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(),
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();
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(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;
}
}
}
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/DependencyInjection/MvcCoreBuilderExtensionsTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/DependencyInjection/MvcCoreBuilderExtensionsTest.cs
index 1cf271d476..4fc6d41572 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/DependencyInjection/MvcCoreBuilderExtensionsTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/DependencyInjection/MvcCoreBuilderExtensionsTest.cs
@@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Mvc.DependencyInjection
Mock.Of(),
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";
- }
}
}
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Infrastructure/DefaultActionSelectorTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Infrastructure/DefaultActionSelectorTests.cs
index 7de2df1f72..366ec69656 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Infrastructure/DefaultActionSelectorTests.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Infrastructure/DefaultActionSelectorTests.cs
@@ -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();
- 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 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();
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs
index e9f4a0962d..d953c8fed6 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs
@@ -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();
- 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();
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 controllerTypes)
+ {
+ var manager = new ApplicationPartManager();
+ manager.ApplicationParts.Add(new TestApplicationPart(controllerTypes));
+ manager.FeatureProviders.Add(new TestFeatureProvider());
+ return manager;
+ }
+
private IEnumerable GetDescriptors(params TypeInfo[] controllerTypeInfos)
{
var provider = GetProvider(controllerTypeInfos);
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerAsServicesTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerAsServicesTest.cs
deleted file mode 100644
index 22998e7e1c..0000000000
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerAsServicesTest.cs
+++ /dev/null
@@ -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(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(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
- {
- }
- }
-}
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/TestApplicationPart.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/TestApplicationPart.cs
new file mode 100644
index 0000000000..b8541d776b
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/TestApplicationPart.cs
@@ -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();
+ }
+
+ public TestApplicationPart(params TypeInfo[] types)
+ {
+ Types = types;
+ }
+
+ public TestApplicationPart(IEnumerable types)
+ {
+ Types = types;
+ }
+
+ public TestApplicationPart(IEnumerable 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 Types { get; }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/TestFeatureProvider.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/TestFeatureProvider.cs
new file mode 100644
index 0000000000..77c87709a4
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/TestFeatureProvider.cs
@@ -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
+ {
+ private readonly Func _filter;
+
+ public TestFeatureProvider()
+ : this(t => true)
+ {
+ }
+
+ public TestFeatureProvider(Func filter)
+ {
+ _filter = filter;
+ }
+
+ public void PopulateFeature(IEnumerable parts, ControllerFeature feature)
+ {
+ foreach (var type in parts.OfType().SelectMany(t => t.Types).Where(_filter))
+ {
+ feature.Controllers.Add(type);
+ }
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/MvcTestFixture.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/MvcTestFixture.cs
index f700b403a0..33a74a5712 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/MvcTestFixture.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/MvcTestFixture.cs
@@ -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);
}
diff --git a/test/Microsoft.AspNetCore.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs b/test/Microsoft.AspNetCore.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs
index e66d33ad7a..a0f894e836 100644
--- a/test/Microsoft.AspNetCore.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs
@@ -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 types)
+ {
+ Types = types;
}
- public override IEnumerable ControllerTypes
+ public override string Name => "Test";
+
+ public IEnumerable Types { get; }
+ }
+
+ private class TestProvider : IApplicationFeatureProvider
+ {
+ public void PopulateFeature(IEnumerable parts, ControllerFeature feature)
{
- get
+ foreach (var type in parts.OfType().SelectMany(t => t.Types))
{
- return base.ControllerTypes
- .Where(typeInfo => typeInfo.Namespace == "System.Web.Http.TestControllers");
+ feature.Controllers.Add(type);
+ }
+ }
+ }
+
+ private class NamespaceFilteredControllersFeatureProvider : IApplicationFeatureProvider
+ {
+ public void PopulateFeature(IEnumerable parts, ControllerFeature feature)
+ {
+ var controllers = feature.Controllers.ToList();
+ foreach (var controller in controllers)
+ {
+ if (controller.Namespace != "System.Web.Http.TestControllers")
+ {
+ feature.Controllers.Remove(controller);
+ }
}
}
}
diff --git a/test/WebSites/ControllersFromServicesWebSite/Startup.cs b/test/WebSites/ControllersFromServicesWebSite/Startup.cs
index 03e9d5b1aa..7339ad8400 100644
--- a/test/WebSites/ControllersFromServicesWebSite/Startup.cs
+++ b/test/WebSites/ControllersFromServicesWebSite/Startup.cs
@@ -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();
services.AddSingleton();
}
+ 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 Types { get; }
+ }
+
public void Configure(IApplicationBuilder app)
{
app.UseCultureReplacer();