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

Commit

Permalink
[Fixes #4014] Add overload to AddControllerAsServices that uses the d…
Browse files Browse the repository at this point in the history
…efault 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.
  • Loading branch information
javiercn committed Mar 31, 2016
1 parent 1bd66ff commit 5632d35
Show file tree
Hide file tree
Showing 24 changed files with 918 additions and 859 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -35,5 +35,8 @@ public AssemblyPart(Assembly assembly)
/// Gets the name of the <see cref="ApplicationPart"/>.
/// </summary>
public override string Name => Assembly.GetName().Name;

/// <inheritdoc />
public IEnumerable<TypeInfo> Types => Assembly.DefinedTypes;
}
}
Original file line number Diff line number Diff line change
@@ -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; }
}
}
24 changes: 24 additions & 0 deletions src/Microsoft.AspNetCore.Mvc.Core/Controllers/ControllerFeature.cs
Original file line number Diff line number Diff line change
@@ -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>();
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -110,70 +110,22 @@ public static IMvcBuilder ConfigureApplicationPartManager(
}

/// <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;
}

/// <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));
}
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());

ControllersAsServices.AddControllersAsServices(builder.Services, controllerAssemblies);
return builder;
}
}
Expand Down
Loading

0 comments on commit 5632d35

Please sign in to comment.