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

Commit ec9dfe4

Browse files
committed
[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.
1 parent 385d50b commit ec9dfe4

26 files changed

+919
-861
lines changed

src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/AssemblyPart.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
1010
/// <summary>
1111
/// An <see cref="ApplicationPart"/> backed by an <see cref="Assembly"/>.
1212
/// </summary>
13-
public class AssemblyPart : ApplicationPart
13+
public class AssemblyPart : ApplicationPart, IApplicationPartTypeProvider
1414
{
1515
/// <summary>
1616
/// Initalizes a new <see cref="AssemblyPart"/> instance.
@@ -35,5 +35,8 @@ public AssemblyPart(Assembly assembly)
3535
/// Gets the name of the <see cref="ApplicationPart"/>.
3636
/// </summary>
3737
public override string Name => Assembly.GetName().Name;
38+
39+
/// <inheritdoc />
40+
public IEnumerable<TypeInfo> Types => Assembly.DefinedTypes;
3841
}
3942
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
using System.Reflection;
6+
7+
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
8+
{
9+
/// <summary>
10+
/// Exposes a set of types from an <see cref="ApplicationPart"/>.
11+
/// </summary>
12+
public interface IApplicationPartTypeProvider
13+
{
14+
/// <summary>
15+
/// Gets the list of available types in the <see cref="ApplicationPart"/>.
16+
/// </summary>
17+
IEnumerable<TypeInfo> Types { get; }
18+
}
19+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
using System.Reflection;
6+
using Microsoft.AspNetCore.Mvc.ApplicationParts;
7+
using Microsoft.Extensions.DependencyInjection;
8+
9+
namespace Microsoft.AspNetCore.Mvc.Controllers
10+
{
11+
/// <summary>
12+
/// The list of controllers types in an MVC application. The <see cref="ControllerFeature"/> can be populated
13+
/// using the <see cref="ApplicationPartManager"/> that is available during startup at <see cref="IMvcBuilder.PartManager"/>
14+
/// and <see cref="IMvcCoreBuilder.PartManager"/> or at a later stage by requiring the <see cref="ApplicationPartManager"/>
15+
/// as a dependency in a component.
16+
/// </summary>
17+
public class ControllerFeature
18+
{
19+
/// <summary>
20+
/// Gets the list of controller types in an MVC application.
21+
/// </summary>
22+
public IList<TypeInfo> Controllers { get; } = new List<TypeInfo>();
23+
}
24+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Reflection;
8+
using Microsoft.AspNetCore.Mvc.ApplicationParts;
9+
10+
namespace Microsoft.AspNetCore.Mvc.Controllers
11+
{
12+
/// <summary>
13+
/// Discovers controllers from a list of <see cref="ApplicationPart"/> instances.
14+
/// </summary>
15+
public class ControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
16+
{
17+
private const string ControllerTypeNameSuffix = "Controller";
18+
19+
/// <inheritdoc />
20+
public void PopulateFeature(
21+
IEnumerable<ApplicationPart> parts,
22+
ControllerFeature feature)
23+
{
24+
foreach (var part in parts.OfType<IApplicationPartTypeProvider>())
25+
{
26+
foreach (var type in part.Types)
27+
{
28+
if (IsController(type) && !feature.Controllers.Contains(type))
29+
{
30+
feature.Controllers.Add(type);
31+
}
32+
}
33+
}
34+
}
35+
36+
/// <summary>
37+
/// Determines if a given <paramref name="typeInfo"/> is a controller.
38+
/// </summary>
39+
/// <param name="typeInfo">The <see cref="TypeInfo"/> candidate.</param>
40+
/// <returns><code>true</code> if the type is a controller; otherwise <code>false</code>.</returns>
41+
protected virtual bool IsController(TypeInfo typeInfo)
42+
{
43+
if (!typeInfo.IsClass)
44+
{
45+
return false;
46+
}
47+
48+
if (typeInfo.IsAbstract)
49+
{
50+
return false;
51+
}
52+
53+
// We only consider public top-level classes as controllers. IsPublic returns false for nested
54+
// classes, regardless of visibility modifiers
55+
if (!typeInfo.IsPublic)
56+
{
57+
return false;
58+
}
59+
60+
if (typeInfo.ContainsGenericParameters)
61+
{
62+
return false;
63+
}
64+
65+
if (typeInfo.IsDefined(typeof(NonControllerAttribute)))
66+
{
67+
return false;
68+
}
69+
70+
if (!typeInfo.Name.EndsWith(ControllerTypeNameSuffix, StringComparison.OrdinalIgnoreCase) &&
71+
!typeInfo.IsDefined(typeof(ControllerAttribute)))
72+
{
73+
return false;
74+
}
75+
76+
return true;
77+
}
78+
}
79+
}

src/Microsoft.AspNetCore.Mvc.Core/Controllers/DefaultControllerTypeProvider.cs

Lines changed: 0 additions & 91 deletions
This file was deleted.

src/Microsoft.AspNetCore.Mvc.Core/Controllers/StaticControllerTypeProvider.cs

Lines changed: 0 additions & 46 deletions
This file was deleted.

src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreMvcBuilderExtensions.cs

Lines changed: 9 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5-
using System.Collections.Generic;
65
using System.Linq;
76
using System.Reflection;
87
using Microsoft.AspNetCore.Mvc;
98
using Microsoft.AspNetCore.Mvc.ApplicationParts;
9+
using Microsoft.AspNetCore.Mvc.Controllers;
1010
using Microsoft.AspNetCore.Mvc.Formatters;
11-
using Microsoft.AspNetCore.Mvc.Internal;
11+
using Microsoft.Extensions.DependencyInjection.Extensions;
1212

1313
namespace Microsoft.Extensions.DependencyInjection
1414
{
@@ -110,70 +110,22 @@ public static IMvcBuilder ConfigureApplicationPartManager(
110110
}
111111

112112
/// <summary>
113-
/// Register the specified <paramref name="controllerTypes"/> as services and as a source for controller
114-
/// discovery.
113+
/// Registers discovered controllers as services in the <see cref="IServiceCollection"/>.
115114
/// </summary>
116115
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
117-
/// <param name="controllerTypes">A sequence of controller <see cref="Type"/>s to register.</param>
118116
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
119-
public static IMvcBuilder AddControllersAsServices(
120-
this IMvcBuilder builder,
121-
params Type[] controllerTypes)
117+
public static IMvcBuilder AddControllersAsServices(this IMvcBuilder builder)
122118
{
123-
return builder.AddControllersAsServices(controllerTypes.AsEnumerable());
124-
}
119+
var feature = new ControllerFeature();
120+
builder.PartManager.PopulateFeature(feature);
125121

126-
/// <summary>
127-
/// Register the specified <paramref name="controllerTypes"/> as services and as a source for controller
128-
/// discovery.
129-
/// </summary>
130-
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
131-
/// <param name="controllerTypes">A sequence of controller <see cref="Type"/>s to register.</param>
132-
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
133-
public static IMvcBuilder AddControllersAsServices(
134-
this IMvcBuilder builder,
135-
IEnumerable<Type> controllerTypes)
122+
foreach (var controller in feature.Controllers.Select(c => c.AsType()))
136123
{
137-
if (builder == null)
138-
{
139-
throw new ArgumentNullException(nameof(builder));
124+
builder.Services.TryAddTransient(controller, controller);
140125
}
141126

142-
ControllersAsServices.AddControllersAsServices(builder.Services, controllerTypes);
143-
return builder;
144-
}
145-
146-
/// <summary>
147-
/// Registers controller types from the specified <paramref name="controllerAssemblies"/> as services and as a source
148-
/// for controller discovery.
149-
/// </summary>
150-
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
151-
/// <param name="controllerAssemblies">Assemblies to scan.</param>
152-
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
153-
public static IMvcBuilder AddControllersAsServices(
154-
this IMvcBuilder builder,
155-
params Assembly[] controllerAssemblies)
156-
{
157-
return builder.AddControllersAsServices(controllerAssemblies.AsEnumerable());
158-
}
159-
160-
/// <summary>
161-
/// Registers controller types from the specified <paramref name="controllerAssemblies"/> as services and as a source
162-
/// for controller discovery.
163-
/// </summary>
164-
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
165-
/// <param name="controllerAssemblies">Assemblies to scan.</param>
166-
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
167-
public static IMvcBuilder AddControllersAsServices(
168-
this IMvcBuilder builder,
169-
IEnumerable<Assembly> controllerAssemblies)
170-
{
171-
if (builder == null)
172-
{
173-
throw new ArgumentNullException(nameof(builder));
174-
}
127+
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
175128

176-
ControllersAsServices.AddControllersAsServices(builder.Services, controllerAssemblies);
177129
return builder;
178130
}
179131
}

0 commit comments

Comments
 (0)