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

Commit

Permalink
[Fixes #4089] Add support for application parts
Browse files Browse the repository at this point in the history
This commit introduces application parts as a concept on MVC.

An application part is an abstraction that allows you to expose some
feature or corncern in a way that is decoupled from their underlying source.
Examples of this include types in an assembly, emdeded resources, files on
disk etc.

Application parts are configured during startup by adding or removing them from
the application part manager available as part of IMvcBuilder and IMvcCoreBuilder.

The application part manager provides the ability to populate features from the
list of available application parts by using a list of application feature providers.
Application feature providers are responsible for populating a given feature given a
list of application parts.

Examples of application providers can be a ControllerFeatureProvider
that goes through the list of application parts, sees which one of those parts exposes types,
determines which of those types are controller types, and adds them to a ControllerFeature
that holds a list of all the types that will be considered controllers in the application.
  • Loading branch information
javiercn committed Mar 30, 2016
1 parent eaaa2c0 commit 385d50b
Show file tree
Hide file tree
Showing 25 changed files with 676 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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.

namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
/// <summary>
/// A part of an MVC application.
/// </summary>
public abstract class ApplicationPart
{
/// <summary>
/// Gets the <see cref="ApplicationPart"/> name.
/// </summary>
public abstract string Name { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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;

namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
/// <summary>
/// Manages the parts and features of an MVC application.
/// </summary>
public class ApplicationPartManager
{
/// <summary>
/// Gets the list of <see cref="IApplicationFeatureProvider"/>s.
/// </summary>
public IList<IApplicationFeatureProvider> FeatureProviders { get; } =
new List<IApplicationFeatureProvider>();

/// <summary>
/// Gets the list of <see cref="ApplicationPart"/>s.
/// </summary>
public IList<ApplicationPart> ApplicationParts { get; } =
new List<ApplicationPart>();

/// <summary>
/// Populates the given <paramref name="feature"/> using the list of
/// <see cref="IApplicationFeatureProvider{TFeature}"/>s configured on the
/// <see cref="ApplicationPartManager"/>.
/// </summary>
/// <typeparam name="TFeature">The type of the feature.</typeparam>
/// <param name="feature">The feature instance to populate.</param>
public void PopulateFeature<TFeature>(TFeature feature)
{
if (feature == null)
{
throw new ArgumentNullException(nameof(feature));
}

foreach (var provider in FeatureProviders.OfType<IApplicationFeatureProvider<TFeature>>())
{
provider.PopulateFeature(ApplicationParts, feature);
}
}
}
}
39 changes: 39 additions & 0 deletions src/Microsoft.AspNetCore.Mvc.Core/ApplicationParts/AssemblyPart.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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;

namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
/// <summary>
/// An <see cref="ApplicationPart"/> backed by an <see cref="Assembly"/>.
/// </summary>
public class AssemblyPart : ApplicationPart
{
/// <summary>
/// Initalizes a new <see cref="AssemblyPart"/> instance.
/// </summary>
/// <param name="assembly"></param>
public AssemblyPart(Assembly assembly)
{
if (assembly == null)
{
throw new ArgumentNullException(nameof(assembly));
}

Assembly = assembly;
}

/// <summary>
/// Gets the <see cref="Assembly"/> of the <see cref="ApplicationPart"/>.
/// </summary>
public Assembly Assembly { get; }

/// <summary>
/// Gets the name of the <see cref="ApplicationPart"/>.
/// </summary>
public override string Name => Assembly.GetName().Name;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// 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.

namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
/// <summary>
/// Marker interface for <see cref="IApplicationFeatureProvider"/>
/// implementations.
/// </summary>
public interface IApplicationFeatureProvider
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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;

namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
/// <summary>
/// A provider for a given <typeparamref name="TFeature"/> feature.
/// </summary>
/// <typeparam name="TFeature">The type of the feature.</typeparam>
public interface IApplicationFeatureProvider<TFeature> : IApplicationFeatureProvider
{
/// <summary>
/// Updates the <paramref name="feature"/> intance.
/// </summary>
/// <param name="parts">The list of <see cref="ApplicationPart"/>s of the
/// application.
/// </param>
/// <param name="feature">The feature instance to populate.</param>
void PopulateFeature(IEnumerable<ApplicationPart> parts, TFeature feature);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// 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 Microsoft.AspNetCore.Mvc.ApplicationParts;

namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
Expand All @@ -12,5 +14,11 @@ public interface IMvcBuilder
/// Gets the <see cref="IServiceCollection"/> where MVC services are configured.
/// </summary>
IServiceCollection Services { get; }

/// <summary>
/// Gets the <see cref="ApplicationPartManager"/> where <see cref="ApplicationPart"/>s
/// are configured.
/// </summary>
ApplicationPartManager PartManager { get; }
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// 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 Microsoft.AspNetCore.Mvc.ApplicationParts;

namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
Expand All @@ -12,5 +14,11 @@ public interface IMvcCoreBuilder
/// Gets the <see cref="IServiceCollection"/> where essential MVC services are configured.
/// </summary>
IServiceCollection Services { get; }

/// <summary>
/// Gets the <see cref="ApplicationPartManager"/> where <see cref="ApplicationPart"/>s
/// are configured.
/// </summary>
ApplicationPartManager PartManager { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@

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.Formatters;
using Microsoft.AspNetCore.Mvc.Internal;
using System.Linq;

namespace Microsoft.Extensions.DependencyInjection
{
Expand Down Expand Up @@ -58,6 +59,56 @@ public static IMvcBuilder AddFormatterMappings(
return builder;
}

/// <summary>
/// Adds an <see cref="ApplicationPart"/> to the list of <see cref="ApplicationPartManager.ApplicationParts"/> on the
/// <see cref="IMvcBuilder.PartManager"/>.
/// </summary>
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
/// <param name="assembly">The <see cref="Assembly"/> of the <see cref="ApplicationPart"/>.</param>
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
public static IMvcBuilder AddApplicationPart(this IMvcBuilder builder, Assembly assembly)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

if (assembly == null)
{
throw new ArgumentNullException(nameof(assembly));
}

builder.ConfigureApplicationPartManager(manager => manager.ApplicationParts.Add(new AssemblyPart(assembly)));

return builder;
}

/// <summary>
/// Configures the <see cref="ApplicationPartManager"/> of the <see cref="IMvcBuilder.PartManager"/> using
/// the given <see cref="Action{ApplicationPartManager}"/>.
/// </summary>
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
/// <param name="setupAction">The <see cref="Action{ApplicationPartManager}"/></param>
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
public static IMvcBuilder ConfigureApplicationPartManager(
this IMvcBuilder builder,
Action<ApplicationPartManager> setupAction)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

if (setupAction == null)
{
throw new ArgumentNullException(nameof(setupAction));
}

setupAction(builder.PartManager);

return builder;
}

/// <summary>
/// Register the specified <paramref name="controllerTypes"/> as services and as a source for controller
/// discovery.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@

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.Formatters;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Linq;

namespace Microsoft.Extensions.DependencyInjection
{
Expand Down Expand Up @@ -143,6 +144,56 @@ public static IMvcCoreBuilder AddControllersAsServices(
return builder.AddControllersAsServices(controllerAssemblies.AsEnumerable());
}

/// <summary>
/// Adds an <see cref="ApplicationPart"/> to the list of <see cref="ApplicationPartManager.ApplicationParts"/> on the
/// <see cref="IMvcCoreBuilder.PartManager"/>.
/// </summary>
/// <param name="builder">The <see cref="IMvcCoreBuilder"/>.</param>
/// <param name="assembly">The <see cref="Assembly"/> of the <see cref="ApplicationPart"/>.</param>
/// <returns>The <see cref="IMvcCoreBuilder"/>.</returns>
public static IMvcCoreBuilder AddApplicationPart(this IMvcCoreBuilder builder, Assembly assembly)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

if (assembly == null)
{
throw new ArgumentNullException(nameof(assembly));
}

builder.ConfigureApplicationPartManager(manager => manager.ApplicationParts.Add(new AssemblyPart(assembly)));

return builder;
}

/// <summary>
/// Configures the <see cref="ApplicationPartManager"/> of the <see cref="IMvcCoreBuilder.PartManager"/> using
/// the given <see cref="Action{ApplicationPartManager}"/>.
/// </summary>
/// <param name="builder">The <see cref="IMvcCoreBuilder"/>.</param>
/// <param name="setupAction">The <see cref="Action{ApplicationPartManager}"/></param>
/// <returns>The <see cref="IMvcCoreBuilder"/>.</returns>
public static IMvcCoreBuilder ConfigureApplicationPartManager(
this IMvcCoreBuilder builder,
Action<ApplicationPartManager> setupAction)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

if (setupAction == null)
{
throw new ArgumentNullException(nameof(setupAction));
}

setupAction(builder.PartManager);

return builder;
}

/// <summary>
/// Registers controller types from the specified <paramref name="controllerAssemblies"/> as services and as a source
/// for controller discovery.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@

using System;
using System.Buffers;
using System.Linq;
using Microsoft.AspNetCore.Hosting;
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.Filters;
using Microsoft.AspNetCore.Mvc.Infrastructure;
Expand Down Expand Up @@ -39,10 +42,45 @@ public static IMvcCoreBuilder AddMvcCore(this IServiceCollection services)
throw new ArgumentNullException(nameof(services));
}

var partManager = GetApplicationPartManager(services);
services.TryAddSingleton(partManager);

ConfigureDefaultServices(services);
AddMvcCoreServices(services);

return new MvcCoreBuilder(services);
var builder = new MvcCoreBuilder(services, partManager);

return builder;
}

private static ApplicationPartManager GetApplicationPartManager(IServiceCollection services)
{
var manager = GetServiceFromCollection<ApplicationPartManager>(services);
if (manager == null)
{
manager = new ApplicationPartManager();

var environment = GetServiceFromCollection<IHostingEnvironment>(services);
if (environment == null)
{
return manager;
}

var assemblies = new DefaultAssemblyProvider(environment).CandidateAssemblies;
foreach (var assembly in assemblies)
{
manager.ApplicationParts.Add(new AssemblyPart(assembly));
}
}

return manager;
}

private static T GetServiceFromCollection<T>(IServiceCollection services)
{
return (T)services
.FirstOrDefault(d => d.ServiceType == typeof(T))
?.ImplementationInstance;
}

/// <summary>
Expand Down
Loading

0 comments on commit 385d50b

Please sign in to comment.