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