From f638c051fa79c293ea7a0a859ed9700db9a18d71 Mon Sep 17 00:00:00 2001 From: jacalvar Date: Thu, 31 Mar 2016 11:04:07 -0700 Subject: [PATCH] Remove IAssemblyProvider and update DNX to work with application parts. * Remove IAssemblyProvider. * Remove DefaultAssemblyProvider in favor of DefaultAssemblyPartDiscoveryProvider. * Update AddMvcDnx to add the list of DNX discovered assemblies to the list of application parts. --- .../MvcCoreServiceCollectionExtensions.cs | 7 +- .../Infrastructure/DefaultAssemblyProvider.cs | 125 ------------------ .../Infrastructure/IAssemblyProvider.cs | 21 --- .../Infrastructure/StaticAssemblyProvider.cs | 21 --- .../DefaultAssemblyPartDiscoveryProvider.cs | 95 +++++++++++++ .../DnxAssemblyProvider.cs | 2 +- .../MvcDnxServiceCollectionExtensions.cs | 28 +++- ...faultAssemblyPartDiscoveryProviderTest.cs} | 117 ++-------------- .../MvcTestFixture.cs | 13 +- 9 files changed, 141 insertions(+), 288 deletions(-) delete mode 100644 src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/DefaultAssemblyProvider.cs delete mode 100644 src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/IAssemblyProvider.cs delete mode 100644 src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/StaticAssemblyProvider.cs create mode 100644 src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultAssemblyPartDiscoveryProvider.cs rename test/Microsoft.AspNetCore.Mvc.Core.Test/{Infrastructure/DefaultAssemblyProviderTests.cs => Internal/DefaultAssemblyPartDiscoveryProviderTest.cs} (50%) diff --git a/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreServiceCollectionExtensions.cs index 01e6b741ea..17b5c83db8 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreServiceCollectionExtensions.cs @@ -74,10 +74,10 @@ namespace Microsoft.Extensions.DependencyInjection return manager; } - var assemblies = new DefaultAssemblyProvider(environment).CandidateAssemblies; - foreach (var assembly in assemblies) + var parts = DefaultAssemblyPartDiscoveryProvider.DiscoverAssemblyParts(environment.ApplicationName); + foreach (var part in parts) { - manager.ApplicationParts.Add(new AssemblyPart(assembly)); + manager.ApplicationParts.Add(part); } } @@ -132,7 +132,6 @@ namespace Microsoft.Extensions.DependencyInjection // Action Discovery // // These are consumed only when creating action descriptors, then they can be de-allocated - services.TryAddTransient(); services.TryAddEnumerable( ServiceDescriptor.Transient()); diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/DefaultAssemblyProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/DefaultAssemblyProvider.cs deleted file mode 100644 index 6660c535ab..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/DefaultAssemblyProvider.cs +++ /dev/null @@ -1,125 +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.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyModel; - -namespace Microsoft.AspNetCore.Mvc.Infrastructure -{ - /// - /// An that uses to discover assemblies that may - /// contain Mvc specific types such as controllers, and view components. - /// - public class DefaultAssemblyProvider : IAssemblyProvider - { - private const string NativeImageSufix = ".ni"; - private readonly Assembly _entryAssembly; - private readonly DependencyContext _dependencyContext; - - /// - /// Initializes a new instance of . - /// - /// The . - public DefaultAssemblyProvider(IHostingEnvironment environment) - : this( - Assembly.Load(new AssemblyName(environment.ApplicationName)), - DependencyContext.Load(Assembly.Load(new AssemblyName(environment.ApplicationName)))) - { - } - - // Internal for unit testing. - internal DefaultAssemblyProvider(Assembly entryAssembly, DependencyContext dependencyContext) - { - _entryAssembly = entryAssembly; - _dependencyContext = dependencyContext; - } - - /// - /// Gets the set of assembly names that are used as root for discovery of - /// MVC controllers, view components and views. - /// - // DefaultControllerTypeProvider uses CandidateAssemblies to determine if the base type of a POCO controller - // lives in an assembly that references MVC. CandidateAssemblies excludes all assemblies from the - // ReferenceAssemblies set. Consequently adding WebApiCompatShim to this set would cause the ApiController to - // fail this test. - protected virtual HashSet ReferenceAssemblies { get; } = new HashSet(StringComparer.Ordinal) - { - "Microsoft.AspNetCore.Mvc", - "Microsoft.AspNetCore.Mvc.Abstractions", - "Microsoft.AspNetCore.Mvc.ApiExplorer", - "Microsoft.AspNetCore.Mvc.Core", - "Microsoft.AspNetCore.Mvc.Cors", - "Microsoft.AspNetCore.Mvc.DataAnnotations", - "Microsoft.AspNetCore.Mvc.Formatters.Json", - "Microsoft.AspNetCore.Mvc.Formatters.Xml", - "Microsoft.AspNetCore.Mvc.Localization", - "Microsoft.AspNetCore.Mvc.Razor", - "Microsoft.AspNetCore.Mvc.Razor.Host", - "Microsoft.AspNetCore.Mvc.TagHelpers", - "Microsoft.AspNetCore.Mvc.ViewFeatures" - }; - - /// - public IEnumerable CandidateAssemblies - { - get - { - if (_dependencyContext == null) - { - // Use the entry assembly as the sole candidate. - return new[] { _entryAssembly }; - } - - return GetCandidateLibraries() - .SelectMany(library => library.RuntimeAssemblyGroups.GetDefaultGroup().AssetPaths) - .Select(Load) - .Where(assembly => assembly != null); - } - } - - /// - /// Returns a list of libraries that references the assemblies in . - /// By default it returns all assemblies that reference any of the primary MVC assemblies - /// while ignoring MVC assemblies. - /// - /// A set of . - // Internal for unit testing - protected internal virtual IEnumerable GetCandidateLibraries() - { - if (ReferenceAssemblies == null) - { - return Enumerable.Empty(); - } - - return _dependencyContext.RuntimeLibraries.Where(IsCandidateLibrary); - } - - private static Assembly Load(string assetPath) - { - var name = Path.GetFileNameWithoutExtension(assetPath); - if (name != null) - { - if (name.EndsWith(NativeImageSufix, StringComparison.OrdinalIgnoreCase)) - { - name = name.Substring(0, name.Length - NativeImageSufix.Length); - } - - return Assembly.Load(new AssemblyName(name)); - } - - return null; - } - - private bool IsCandidateLibrary(RuntimeLibrary library) - { - Debug.Assert(ReferenceAssemblies != null); - return library.Dependencies.Any(dependency => ReferenceAssemblies.Contains(dependency.Name)); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/IAssemblyProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/IAssemblyProvider.cs deleted file mode 100644 index f100cf0f81..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/IAssemblyProvider.cs +++ /dev/null @@ -1,21 +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.Collections.Generic; -using System.Reflection; - -namespace Microsoft.AspNetCore.Mvc.Infrastructure -{ - /// - /// Specifies the contract for discovering assemblies that may contain Mvc specific types such as controllers, - /// view components and precompiled views. - /// - public interface IAssemblyProvider - { - /// - /// Gets the sequence of candidate instances that the application - /// uses for discovery of Mvc specific types. - /// - IEnumerable CandidateAssemblies { get; } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/StaticAssemblyProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/StaticAssemblyProvider.cs deleted file mode 100644 index 3c5a4eddc3..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/StaticAssemblyProvider.cs +++ /dev/null @@ -1,21 +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.Collections.Generic; -using System.Reflection; - -namespace Microsoft.AspNetCore.Mvc.Infrastructure -{ - /// - /// A with a fixed set of candidate assemblies. - /// - public class StaticAssemblyProvider : IAssemblyProvider - { - /// - /// Gets the list of candidate assemblies. - /// - public IList CandidateAssemblies { get; } = new List(); - - IEnumerable IAssemblyProvider.CandidateAssemblies => CandidateAssemblies; - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultAssemblyPartDiscoveryProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultAssemblyPartDiscoveryProvider.cs new file mode 100644 index 0000000000..75a58a376e --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultAssemblyPartDiscoveryProvider.cs @@ -0,0 +1,95 @@ +// 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.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using Microsoft.Extensions.DependencyModel; + +namespace Microsoft.AspNetCore.Mvc.Internal +{ + // Discovers assemblies that are part of the MVC application using the DependencyContext. + public static class DefaultAssemblyPartDiscoveryProvider + { + private const string NativeImageSufix = ".ni"; + + internal static HashSet ReferenceAssemblies { get; } = new HashSet(StringComparer.Ordinal) + { + "Microsoft.AspNetCore.Mvc", + "Microsoft.AspNetCore.Mvc.Abstractions", + "Microsoft.AspNetCore.Mvc.ApiExplorer", + "Microsoft.AspNetCore.Mvc.Core", + "Microsoft.AspNetCore.Mvc.Cors", + "Microsoft.AspNetCore.Mvc.DataAnnotations", + "Microsoft.AspNetCore.Mvc.Formatters.Json", + "Microsoft.AspNetCore.Mvc.Formatters.Xml", + "Microsoft.AspNetCore.Mvc.Localization", + "Microsoft.AspNetCore.Mvc.Razor", + "Microsoft.AspNetCore.Mvc.Razor.Host", + "Microsoft.AspNetCore.Mvc.TagHelpers", + "Microsoft.AspNetCore.Mvc.ViewFeatures" + }; + + public static IEnumerable DiscoverAssemblyParts(string entryPointAssemblyName) + { + var entryAssembly = Assembly.Load(new AssemblyName(entryPointAssemblyName)); + var context = DependencyContext.Load(Assembly.Load(new AssemblyName(entryPointAssemblyName))); + + return GetCandidateAssemblies(entryAssembly, context).Select(p => new AssemblyPart(p)); + } + + internal static IEnumerable GetCandidateAssemblies(Assembly entryAssembly, DependencyContext dependencyContext) + { + if (dependencyContext == null) + { + // Use the entry assembly as the sole candidate. + return new[] { entryAssembly }; + } + + return GetCandidateLibraries(dependencyContext) + .SelectMany(library => library.RuntimeAssemblyGroups.GetDefaultGroup().AssetPaths) + .Select(Load) + .Where(assembly => assembly != null); + } + + // Returns a list of libraries that references the assemblies in . + // By default it returns all assemblies that reference any of the primary MVC assemblies + // while ignoring MVC assemblies. + // Internal for unit testing + internal static IEnumerable GetCandidateLibraries(DependencyContext dependencyContext) + { + if (ReferenceAssemblies == null) + { + return Enumerable.Empty(); + } + + return dependencyContext.RuntimeLibraries.Where(IsCandidateLibrary); + } + + private static Assembly Load(string assetPath) + { + var name = Path.GetFileNameWithoutExtension(assetPath); + if (name != null) + { + if (name.EndsWith(NativeImageSufix, StringComparison.OrdinalIgnoreCase)) + { + name = name.Substring(0, name.Length - NativeImageSufix.Length); + } + + return Assembly.Load(new AssemblyName(name)); + } + + return null; + } + + private static bool IsCandidateLibrary(RuntimeLibrary library) + { + Debug.Assert(ReferenceAssemblies != null); + return library.Dependencies.Any(dependency => ReferenceAssemblies.Contains(dependency.Name)); + } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.Dnx/DnxAssemblyProvider.cs b/src/Microsoft.AspNetCore.Mvc.Dnx/DnxAssemblyProvider.cs index a28590fb08..ca4a26025d 100644 --- a/src/Microsoft.AspNetCore.Mvc.Dnx/DnxAssemblyProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.Dnx/DnxAssemblyProvider.cs @@ -10,7 +10,7 @@ using Microsoft.Extensions.PlatformAbstractions; namespace Microsoft.AspNetCore.Mvc.Infrastructure { - public class DnxAssemblyProvider : IAssemblyProvider + public class DnxAssemblyProvider { private readonly ILibraryManager _libraryManager; diff --git a/src/Microsoft.AspNetCore.Mvc.Dnx/MvcDnxServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Dnx/MvcDnxServiceCollectionExtensions.cs index be216f209a..7af08d2f84 100644 --- a/src/Microsoft.AspNetCore.Mvc.Dnx/MvcDnxServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNetCore.Mvc.Dnx/MvcDnxServiceCollectionExtensions.cs @@ -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 System.Linq; +using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.AspNetCore.Mvc.Razor.Internal; @@ -20,9 +22,15 @@ namespace Microsoft.Extensions.DependencyInjection { if (DnxPlatformServices.Default.LibraryManager != null) { + var partManager = GetApplicationPartManager(services); + var provider = new DnxAssemblyProvider(DnxPlatformServices.Default.LibraryManager); + foreach (var assembly in provider.CandidateAssemblies) + { + partManager.ApplicationParts.Add(new AssemblyPart(assembly)); + } + // Add IAssemblyProvider services services.AddSingleton(DnxPlatformServices.Default.LibraryManager); - services.AddTransient(); // Add compilation services services.AddSingleton(CompilationServices.Default.LibraryExporter); @@ -31,5 +39,23 @@ namespace Microsoft.Extensions.DependencyInjection return services; } + + private static ApplicationPartManager GetApplicationPartManager(IServiceCollection services) + { + var manager = GetServiceFromCollection(services); + if (manager == null) + { + manager = new ApplicationPartManager(); + } + + return manager; + } + + private static T GetServiceFromCollection(IServiceCollection services) + { + return (T)services + .FirstOrDefault(d => d.ServiceType == typeof(T)) + ?.ImplementationInstance; + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Infrastructure/DefaultAssemblyProviderTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultAssemblyPartDiscoveryProviderTest.cs similarity index 50% rename from test/Microsoft.AspNetCore.Mvc.Core.Test/Infrastructure/DefaultAssemblyProviderTests.cs rename to test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultAssemblyPartDiscoveryProviderTest.cs index ba24486644..9179a3cd2d 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Infrastructure/DefaultAssemblyProviderTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultAssemblyPartDiscoveryProviderTest.cs @@ -1,18 +1,18 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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.Extensions.DependencyModel; using Xunit; -namespace Microsoft.AspNetCore.Mvc.Infrastructure +namespace Microsoft.AspNetCore.Mvc.Internal { - public class DefaultAssemblyProviderTests + public class DefaultAssemblyPartDiscoveryProviderTests { - private static readonly Assembly CurrentAssembly = typeof(DefaultAssemblyProviderTests).GetTypeInfo().Assembly; + private static readonly Assembly CurrentAssembly = + typeof(DefaultAssemblyPartDiscoveryProviderTests).GetTypeInfo().Assembly; [Fact] public void GetCandidateLibraries_IgnoresMvcAssemblies() @@ -31,10 +31,9 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure expected, }, Enumerable.Empty()); - var provider = new DefaultAssemblyProvider(CurrentAssembly, dependencyContext); // Act - var candidates = provider.GetCandidateLibraries(); + var candidates = DefaultAssemblyPartDiscoveryProvider.GetCandidateLibraries(dependencyContext); // Assert Assert.Equal(new[] { expected }, candidates); @@ -43,11 +42,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure [Fact] public void CandidateAssemblies_ReturnsEntryAssemblyIfDependencyContextIsNull() { - // Arrange - var provider = new DefaultAssemblyProvider(CurrentAssembly, dependencyContext: null); - - // Act - var candidates = provider.CandidateAssemblies; + // Arrange & Act + var candidates = DefaultAssemblyPartDiscoveryProvider.GetCandidateAssemblies(CurrentAssembly, dependencyContext: null); // Assert Assert.Equal(new[] { CurrentAssembly }, candidates); @@ -69,82 +65,21 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure GetLibrary("Baz", "Microsoft.AspNetCore.Mvc.Abstractions"), }, Enumerable.Empty()); - var provider = new DefaultAssemblyProvider(CurrentAssembly, dependencyContext); // Act - var candidates = provider.GetCandidateLibraries(); + var candidates = DefaultAssemblyPartDiscoveryProvider.GetCandidateLibraries(dependencyContext); // Assert Assert.Equal(new[] { "Foo", "Bar", "Baz" }, candidates.Select(a => a.Name)); } - [Fact] - public void GetCandidateLibraries_ReturnsLibrariesReferencingOverriddenAssemblies() - { - // Arrange - var dependencyContext = new DependencyContext( - new TargetInfo("framework", "runtime", "signature", isPortable: true), - CompilationOptions.Default, - new CompilationLibrary[0], - new[] - { - GetLibrary("Foo", "CustomMvc.Modules"), - GetLibrary("Bar", "CustomMvc.Application.Loader"), - GetLibrary("Baz", "Microsoft.AspNetCore.Mvc.Abstractions"), - }, - Enumerable.Empty()); - var referenceAssemblies = new HashSet - { - "CustomMvc.Modules", - "CustomMvc.Application.Loader" - }; - var assemblyProvider = new OverridenAssemblyProvider( - CurrentAssembly, - dependencyContext, - referenceAssemblies); - - // Act - var candidates = assemblyProvider.GetCandidateLibraries(); - - // Assert - Assert.Equal(new[] { "Foo", "Bar" }, candidates.Select(a => a.Name)); - } - - [Fact] - public void GetCandidateLibraries_ReturnsEmptySequenceWhenReferenceAssembliesIsNull() - { - // Arrange - var dependencyContext = new DependencyContext( - new TargetInfo("framework", "runtime", "signature", isPortable: true), - CompilationOptions.Default, - new CompilationLibrary[0], - new[] - { - GetLibrary("Foo", "CustomMvc.Modules"), - GetLibrary("Bar", "CustomMvc.Application.Loader"), - GetLibrary("Baz", "Microsoft.AspNetCore.Mvc.Abstractions"), - }, - Enumerable.Empty()); - var assemblyProvider = new OverridenAssemblyProvider( - CurrentAssembly, - dependencyContext, - referenceAssemblies: null); - - // Act - var candidates = assemblyProvider.GetCandidateLibraries(); - - // Assert - Assert.Empty(candidates); - } - - // This test verifies DefaultAssemblyProvider.ReferenceAssemblies reflects the actual loadable assemblies + // This test verifies DefaultAssemblyPartDiscoveryProvider.ReferenceAssemblies reflects the actual loadable assemblies // of the libraries that Microsoft.AspNetCore.Mvc dependes on. // If we add or remove dependencies, this test should be changed together. [Fact] public void ReferenceAssemblies_ReturnsLoadableReferenceAssemblies() { // Arrange - var provider = new TestableAssemblyProvider(CurrentAssembly, dependencyContext: null); var excludeAssemblies = new string[] { "Microsoft.AspNetCore.Mvc.WebApiCompatShim", @@ -170,7 +105,9 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure .OrderBy(p => p, StringComparer.Ordinal); // Act - var referenceAssemblies = provider.ReferenceAssemblies.OrderBy(p => p, StringComparer.Ordinal); + var referenceAssemblies = DefaultAssemblyPartDiscoveryProvider + .ReferenceAssemblies + .OrderBy(p => p, StringComparer.Ordinal); // Assert Assert.Equal(expected, referenceAssemblies); @@ -191,31 +128,5 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure dependencies: dependencies.ToArray(), serviceable: true); } - - private class OverridenAssemblyProvider : DefaultAssemblyProvider - { - public OverridenAssemblyProvider( - Assembly entryAssembly, - DependencyContext dependencyContext, - HashSet referenceAssemblies) - : base(entryAssembly, dependencyContext) - { - ReferenceAssemblies = referenceAssemblies; - } - - protected override HashSet ReferenceAssemblies { get; } - } - - private class TestableAssemblyProvider : DefaultAssemblyProvider - { - public TestableAssemblyProvider( - Assembly entryAssembly, - DependencyContext dependencyContext) - : base(entryAssembly, dependencyContext) - { - } - - public new HashSet ReferenceAssemblies => base.ReferenceAssemblies; - } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/MvcTestFixture.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/MvcTestFixture.cs index 2d48dda86c..2a3c90d973 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/MvcTestFixture.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/MvcTestFixture.cs @@ -66,11 +66,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests { var startupAssembly = typeof(TStartup).GetTypeInfo().Assembly; - // Inject a custom assembly provider. Overrides AddMvc() because that uses TryAdd(). - var assemblyProvider = new StaticAssemblyProvider(); - assemblyProvider.CandidateAssemblies.Add(startupAssembly); - services.AddSingleton(assemblyProvider); - + // Inject a custom application part manager. Overrides AddMvcCore() because that uses TryAdd(). var manager = new ApplicationPartManager(); manager.ApplicationParts.Add(new AssemblyPart(startupAssembly)); @@ -79,12 +75,5 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests services.AddSingleton(manager); } - - private class StaticAssemblyProvider : IAssemblyProvider - { - public IList CandidateAssemblies { get; } = new List(); - - IEnumerable IAssemblyProvider.CandidateAssemblies => CandidateAssemblies; - } } }