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

Commit 385d50b

Browse files
committed
[Fixes #4089] Add support for application parts
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.
1 parent eaaa2c0 commit 385d50b

File tree

25 files changed

+676
-17
lines changed

25 files changed

+676
-17
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
5+
{
6+
/// <summary>
7+
/// A part of an MVC application.
8+
/// </summary>
9+
public abstract class ApplicationPart
10+
{
11+
/// <summary>
12+
/// Gets the <see cref="ApplicationPart"/> name.
13+
/// </summary>
14+
public abstract string Name { get; }
15+
}
16+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
8+
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
9+
{
10+
/// <summary>
11+
/// Manages the parts and features of an MVC application.
12+
/// </summary>
13+
public class ApplicationPartManager
14+
{
15+
/// <summary>
16+
/// Gets the list of <see cref="IApplicationFeatureProvider"/>s.
17+
/// </summary>
18+
public IList<IApplicationFeatureProvider> FeatureProviders { get; } =
19+
new List<IApplicationFeatureProvider>();
20+
21+
/// <summary>
22+
/// Gets the list of <see cref="ApplicationPart"/>s.
23+
/// </summary>
24+
public IList<ApplicationPart> ApplicationParts { get; } =
25+
new List<ApplicationPart>();
26+
27+
/// <summary>
28+
/// Populates the given <paramref name="feature"/> using the list of
29+
/// <see cref="IApplicationFeatureProvider{TFeature}"/>s configured on the
30+
/// <see cref="ApplicationPartManager"/>.
31+
/// </summary>
32+
/// <typeparam name="TFeature">The type of the feature.</typeparam>
33+
/// <param name="feature">The feature instance to populate.</param>
34+
public void PopulateFeature<TFeature>(TFeature feature)
35+
{
36+
if (feature == null)
37+
{
38+
throw new ArgumentNullException(nameof(feature));
39+
}
40+
41+
foreach (var provider in FeatureProviders.OfType<IApplicationFeatureProvider<TFeature>>())
42+
{
43+
provider.PopulateFeature(ApplicationParts, feature);
44+
}
45+
}
46+
}
47+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.Reflection;
7+
8+
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
9+
{
10+
/// <summary>
11+
/// An <see cref="ApplicationPart"/> backed by an <see cref="Assembly"/>.
12+
/// </summary>
13+
public class AssemblyPart : ApplicationPart
14+
{
15+
/// <summary>
16+
/// Initalizes a new <see cref="AssemblyPart"/> instance.
17+
/// </summary>
18+
/// <param name="assembly"></param>
19+
public AssemblyPart(Assembly assembly)
20+
{
21+
if (assembly == null)
22+
{
23+
throw new ArgumentNullException(nameof(assembly));
24+
}
25+
26+
Assembly = assembly;
27+
}
28+
29+
/// <summary>
30+
/// Gets the <see cref="Assembly"/> of the <see cref="ApplicationPart"/>.
31+
/// </summary>
32+
public Assembly Assembly { get; }
33+
34+
/// <summary>
35+
/// Gets the name of the <see cref="ApplicationPart"/>.
36+
/// </summary>
37+
public override string Name => Assembly.GetName().Name;
38+
}
39+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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+
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
5+
{
6+
/// <summary>
7+
/// Marker interface for <see cref="IApplicationFeatureProvider"/>
8+
/// implementations.
9+
/// </summary>
10+
public interface IApplicationFeatureProvider
11+
{
12+
}
13+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
6+
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
7+
{
8+
/// <summary>
9+
/// A provider for a given <typeparamref name="TFeature"/> feature.
10+
/// </summary>
11+
/// <typeparam name="TFeature">The type of the feature.</typeparam>
12+
public interface IApplicationFeatureProvider<TFeature> : IApplicationFeatureProvider
13+
{
14+
/// <summary>
15+
/// Updates the <paramref name="feature"/> intance.
16+
/// </summary>
17+
/// <param name="parts">The list of <see cref="ApplicationPart"/>s of the
18+
/// application.
19+
/// </param>
20+
/// <param name="feature">The feature instance to populate.</param>
21+
void PopulateFeature(IEnumerable<ApplicationPart> parts, TFeature feature);
22+
}
23+
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using Microsoft.AspNetCore.Mvc.ApplicationParts;
5+
46
namespace Microsoft.Extensions.DependencyInjection
57
{
68
/// <summary>
@@ -12,5 +14,11 @@ public interface IMvcBuilder
1214
/// Gets the <see cref="IServiceCollection"/> where MVC services are configured.
1315
/// </summary>
1416
IServiceCollection Services { get; }
17+
18+
/// <summary>
19+
/// Gets the <see cref="ApplicationPartManager"/> where <see cref="ApplicationPart"/>s
20+
/// are configured.
21+
/// </summary>
22+
ApplicationPartManager PartManager { get; }
1523
}
1624
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using Microsoft.AspNetCore.Mvc.ApplicationParts;
5+
46
namespace Microsoft.Extensions.DependencyInjection
57
{
68
/// <summary>
@@ -12,5 +14,11 @@ public interface IMvcCoreBuilder
1214
/// Gets the <see cref="IServiceCollection"/> where essential MVC services are configured.
1315
/// </summary>
1416
IServiceCollection Services { get; }
17+
18+
/// <summary>
19+
/// Gets the <see cref="ApplicationPartManager"/> where <see cref="ApplicationPart"/>s
20+
/// are configured.
21+
/// </summary>
22+
ApplicationPartManager PartManager { get; }
1523
}
1624
}

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

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Linq;
67
using System.Reflection;
78
using Microsoft.AspNetCore.Mvc;
9+
using Microsoft.AspNetCore.Mvc.ApplicationParts;
810
using Microsoft.AspNetCore.Mvc.Formatters;
911
using Microsoft.AspNetCore.Mvc.Internal;
10-
using System.Linq;
1112

1213
namespace Microsoft.Extensions.DependencyInjection
1314
{
@@ -58,6 +59,56 @@ public static IMvcBuilder AddFormatterMappings(
5859
return builder;
5960
}
6061

62+
/// <summary>
63+
/// Adds an <see cref="ApplicationPart"/> to the list of <see cref="ApplicationPartManager.ApplicationParts"/> on the
64+
/// <see cref="IMvcBuilder.PartManager"/>.
65+
/// </summary>
66+
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
67+
/// <param name="assembly">The <see cref="Assembly"/> of the <see cref="ApplicationPart"/>.</param>
68+
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
69+
public static IMvcBuilder AddApplicationPart(this IMvcBuilder builder, Assembly assembly)
70+
{
71+
if (builder == null)
72+
{
73+
throw new ArgumentNullException(nameof(builder));
74+
}
75+
76+
if (assembly == null)
77+
{
78+
throw new ArgumentNullException(nameof(assembly));
79+
}
80+
81+
builder.ConfigureApplicationPartManager(manager => manager.ApplicationParts.Add(new AssemblyPart(assembly)));
82+
83+
return builder;
84+
}
85+
86+
/// <summary>
87+
/// Configures the <see cref="ApplicationPartManager"/> of the <see cref="IMvcBuilder.PartManager"/> using
88+
/// the given <see cref="Action{ApplicationPartManager}"/>.
89+
/// </summary>
90+
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
91+
/// <param name="setupAction">The <see cref="Action{ApplicationPartManager}"/></param>
92+
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
93+
public static IMvcBuilder ConfigureApplicationPartManager(
94+
this IMvcBuilder builder,
95+
Action<ApplicationPartManager> setupAction)
96+
{
97+
if (builder == null)
98+
{
99+
throw new ArgumentNullException(nameof(builder));
100+
}
101+
102+
if (setupAction == null)
103+
{
104+
throw new ArgumentNullException(nameof(setupAction));
105+
}
106+
107+
setupAction(builder.PartManager);
108+
109+
return builder;
110+
}
111+
61112
/// <summary>
62113
/// Register the specified <paramref name="controllerTypes"/> as services and as a source for controller
63114
/// discovery.

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

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Linq;
67
using System.Reflection;
78
using Microsoft.AspNetCore.Authorization;
89
using Microsoft.AspNetCore.Mvc;
910
using Microsoft.AspNetCore.Mvc.ApplicationModels;
11+
using Microsoft.AspNetCore.Mvc.ApplicationParts;
1012
using Microsoft.AspNetCore.Mvc.Formatters;
1113
using Microsoft.AspNetCore.Mvc.Internal;
1214
using Microsoft.Extensions.DependencyInjection.Extensions;
13-
using System.Linq;
1415

1516
namespace Microsoft.Extensions.DependencyInjection
1617
{
@@ -143,6 +144,56 @@ public static IMvcCoreBuilder AddControllersAsServices(
143144
return builder.AddControllersAsServices(controllerAssemblies.AsEnumerable());
144145
}
145146

147+
/// <summary>
148+
/// Adds an <see cref="ApplicationPart"/> to the list of <see cref="ApplicationPartManager.ApplicationParts"/> on the
149+
/// <see cref="IMvcCoreBuilder.PartManager"/>.
150+
/// </summary>
151+
/// <param name="builder">The <see cref="IMvcCoreBuilder"/>.</param>
152+
/// <param name="assembly">The <see cref="Assembly"/> of the <see cref="ApplicationPart"/>.</param>
153+
/// <returns>The <see cref="IMvcCoreBuilder"/>.</returns>
154+
public static IMvcCoreBuilder AddApplicationPart(this IMvcCoreBuilder builder, Assembly assembly)
155+
{
156+
if (builder == null)
157+
{
158+
throw new ArgumentNullException(nameof(builder));
159+
}
160+
161+
if (assembly == null)
162+
{
163+
throw new ArgumentNullException(nameof(assembly));
164+
}
165+
166+
builder.ConfigureApplicationPartManager(manager => manager.ApplicationParts.Add(new AssemblyPart(assembly)));
167+
168+
return builder;
169+
}
170+
171+
/// <summary>
172+
/// Configures the <see cref="ApplicationPartManager"/> of the <see cref="IMvcCoreBuilder.PartManager"/> using
173+
/// the given <see cref="Action{ApplicationPartManager}"/>.
174+
/// </summary>
175+
/// <param name="builder">The <see cref="IMvcCoreBuilder"/>.</param>
176+
/// <param name="setupAction">The <see cref="Action{ApplicationPartManager}"/></param>
177+
/// <returns>The <see cref="IMvcCoreBuilder"/>.</returns>
178+
public static IMvcCoreBuilder ConfigureApplicationPartManager(
179+
this IMvcCoreBuilder builder,
180+
Action<ApplicationPartManager> setupAction)
181+
{
182+
if (builder == null)
183+
{
184+
throw new ArgumentNullException(nameof(builder));
185+
}
186+
187+
if (setupAction == null)
188+
{
189+
throw new ArgumentNullException(nameof(setupAction));
190+
}
191+
192+
setupAction(builder.PartManager);
193+
194+
return builder;
195+
}
196+
146197
/// <summary>
147198
/// Registers controller types from the specified <paramref name="controllerAssemblies"/> as services and as a source
148199
/// for controller discovery.

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

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33

44
using System;
55
using System.Buffers;
6+
using System.Linq;
7+
using Microsoft.AspNetCore.Hosting;
68
using Microsoft.AspNetCore.Mvc;
79
using Microsoft.AspNetCore.Mvc.Abstractions;
810
using Microsoft.AspNetCore.Mvc.ActionConstraints;
911
using Microsoft.AspNetCore.Mvc.ApplicationModels;
12+
using Microsoft.AspNetCore.Mvc.ApplicationParts;
1013
using Microsoft.AspNetCore.Mvc.Controllers;
1114
using Microsoft.AspNetCore.Mvc.Filters;
1215
using Microsoft.AspNetCore.Mvc.Infrastructure;
@@ -39,10 +42,45 @@ public static IMvcCoreBuilder AddMvcCore(this IServiceCollection services)
3942
throw new ArgumentNullException(nameof(services));
4043
}
4144

45+
var partManager = GetApplicationPartManager(services);
46+
services.TryAddSingleton(partManager);
47+
4248
ConfigureDefaultServices(services);
4349
AddMvcCoreServices(services);
4450

45-
return new MvcCoreBuilder(services);
51+
var builder = new MvcCoreBuilder(services, partManager);
52+
53+
return builder;
54+
}
55+
56+
private static ApplicationPartManager GetApplicationPartManager(IServiceCollection services)
57+
{
58+
var manager = GetServiceFromCollection<ApplicationPartManager>(services);
59+
if (manager == null)
60+
{
61+
manager = new ApplicationPartManager();
62+
63+
var environment = GetServiceFromCollection<IHostingEnvironment>(services);
64+
if (environment == null)
65+
{
66+
return manager;
67+
}
68+
69+
var assemblies = new DefaultAssemblyProvider(environment).CandidateAssemblies;
70+
foreach (var assembly in assemblies)
71+
{
72+
manager.ApplicationParts.Add(new AssemblyPart(assembly));
73+
}
74+
}
75+
76+
return manager;
77+
}
78+
79+
private static T GetServiceFromCollection<T>(IServiceCollection services)
80+
{
81+
return (T)services
82+
.FirstOrDefault(d => d.ServiceType == typeof(T))
83+
?.ImplementationInstance;
4684
}
4785

4886
/// <summary>

0 commit comments

Comments
 (0)