Use attributes for application part discovery (#10271)
* Use attributes for application part discovery Fixes https://github.com/aspnet/AspNetCore/issues/4332
This commit is contained in:
parent
e92b4800df
commit
200c9e21bd
|
|
@ -56,9 +56,6 @@
|
||||||
<ExternalAspNetCoreAppReference Include="System.Security.Cryptography.Xml" Version="$(SystemSecurityCryptographyXmlPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="System.Security.Cryptography.Xml" Version="$(SystemSecurityCryptographyXmlPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="System.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="System.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" />
|
||||||
|
|
||||||
<!-- Dependencies from dotnet/core-setup -->
|
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelPackageVersion)" />
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Transitive dependencies of other assemblies in the shared framework. These are listed separately and should not be included directly
|
Transitive dependencies of other assemblies in the shared framework. These are listed separately and should not be included directly
|
||||||
when setting `<Reference>`. These are listed for the purpose of tests and servicing builds only.
|
when setting `<Reference>`. These are listed for the purpose of tests and servicing builds only.
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
<Reference Include="Microsoft.AspNetCore.Routing.Abstractions" />
|
<Reference Include="Microsoft.AspNetCore.Routing.Abstractions" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Routing" />
|
<Reference Include="Microsoft.AspNetCore.Routing" />
|
||||||
<Reference Include="Microsoft.Extensions.DependencyInjection" />
|
<Reference Include="Microsoft.Extensions.DependencyInjection" />
|
||||||
<Reference Include="Microsoft.Extensions.DependencyModel" />
|
|
||||||
<Reference Include="Microsoft.Extensions.FileProviders.Abstractions" />
|
<Reference Include="Microsoft.Extensions.FileProviders.Abstractions" />
|
||||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||||
<Reference Include="Microsoft.Extensions.ParameterDefaultValue.Sources" />
|
<Reference Include="Microsoft.Extensions.ParameterDefaultValue.Sources" />
|
||||||
|
|
|
||||||
|
|
@ -1547,6 +1547,12 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
protected ApplicationPart() { }
|
protected ApplicationPart() { }
|
||||||
public abstract string Name { get; }
|
public abstract string Name { get; }
|
||||||
}
|
}
|
||||||
|
[System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=true)]
|
||||||
|
public sealed partial class ApplicationPartAttribute : System.Attribute
|
||||||
|
{
|
||||||
|
public ApplicationPartAttribute(string assemblyName) { }
|
||||||
|
public string AssemblyName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
|
||||||
|
}
|
||||||
public abstract partial class ApplicationPartFactory
|
public abstract partial class ApplicationPartFactory
|
||||||
{
|
{
|
||||||
protected ApplicationPartFactory() { }
|
protected ApplicationPartFactory() { }
|
||||||
|
|
@ -1560,13 +1566,12 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
public System.Collections.Generic.IList<Microsoft.AspNetCore.Mvc.ApplicationParts.IApplicationFeatureProvider> FeatureProviders { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
|
public System.Collections.Generic.IList<Microsoft.AspNetCore.Mvc.ApplicationParts.IApplicationFeatureProvider> FeatureProviders { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
|
||||||
public void PopulateFeature<TFeature>(TFeature feature) { }
|
public void PopulateFeature<TFeature>(TFeature feature) { }
|
||||||
}
|
}
|
||||||
public partial class AssemblyPart : Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPart, Microsoft.AspNetCore.Mvc.ApplicationParts.IApplicationPartTypeProvider, Microsoft.AspNetCore.Mvc.ApplicationParts.ICompilationReferencesProvider
|
public partial class AssemblyPart : Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPart, Microsoft.AspNetCore.Mvc.ApplicationParts.IApplicationPartTypeProvider
|
||||||
{
|
{
|
||||||
public AssemblyPart(System.Reflection.Assembly assembly) { }
|
public AssemblyPart(System.Reflection.Assembly assembly) { }
|
||||||
public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
|
public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
|
||||||
public override string Name { get { throw null; } }
|
public override string Name { get { throw null; } }
|
||||||
public System.Collections.Generic.IEnumerable<System.Reflection.TypeInfo> Types { get { throw null; } }
|
public System.Collections.Generic.IEnumerable<System.Reflection.TypeInfo> Types { get { throw null; } }
|
||||||
public System.Collections.Generic.IEnumerable<string> GetReferencePaths() { throw null; }
|
|
||||||
}
|
}
|
||||||
public partial class DefaultApplicationPartFactory : Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartFactory
|
public partial class DefaultApplicationPartFactory : Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartFactory
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,295 +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.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Core;
|
|
||||||
using Microsoft.Extensions.DependencyModel;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
|
||||||
{
|
|
||||||
internal class ApplicationAssembliesProvider
|
|
||||||
{
|
|
||||||
internal static HashSet<string> ReferenceAssemblies { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
|
||||||
{
|
|
||||||
// The deps file for the Microsoft.AspNetCore.App shared runtime is authored in a way where it does not say
|
|
||||||
// it depends on Microsoft.AspNetCore.Mvc even though it does. Explicitly list it so that referencing this runtime causes
|
|
||||||
// assembly discovery to work correctly.
|
|
||||||
"Microsoft.AspNetCore.App",
|
|
||||||
"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.NewtonsoftJson",
|
|
||||||
"Microsoft.AspNetCore.Mvc.Razor",
|
|
||||||
"Microsoft.AspNetCore.Mvc.RazorPages",
|
|
||||||
"Microsoft.AspNetCore.Mvc.TagHelpers",
|
|
||||||
"Microsoft.AspNetCore.Mvc.ViewFeatures",
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns an ordered list of application assemblies.
|
|
||||||
/// <para>
|
|
||||||
/// The order is as follows:
|
|
||||||
/// * Entry assembly
|
|
||||||
/// * Assemblies specified in the application's deps file ordered by name.
|
|
||||||
/// <para>
|
|
||||||
/// Each assembly is immediately followed by assemblies specified by annotated <see cref="RelatedAssemblyAttribute"/> ordered by name.
|
|
||||||
/// </para>
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<Assembly> ResolveAssemblies(Assembly entryAssembly)
|
|
||||||
{
|
|
||||||
var dependencyContext = LoadDependencyContext(entryAssembly);
|
|
||||||
|
|
||||||
IEnumerable<AssemblyItem> assemblyItems;
|
|
||||||
|
|
||||||
if (dependencyContext == null || dependencyContext.CompileLibraries.Count == 0)
|
|
||||||
{
|
|
||||||
// If an application was built with PreserveCompilationContext = false, CompileLibraries will be empty and we
|
|
||||||
// can no longer reliably infer the dependency closure. In this case, treat it the same as a missing
|
|
||||||
// deps file.
|
|
||||||
assemblyItems = new[] { GetAssemblyItem(entryAssembly) };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assemblyItems = ResolveFromDependencyContext(dependencyContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
assemblyItems = assemblyItems
|
|
||||||
.OrderBy(item => item.Assembly == entryAssembly ? 0 : 1)
|
|
||||||
.ThenBy(item => item.Assembly.FullName, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
foreach (var item in assemblyItems)
|
|
||||||
{
|
|
||||||
yield return item.Assembly;
|
|
||||||
|
|
||||||
foreach (var associatedAssembly in item.RelatedAssemblies.Distinct().OrderBy(assembly => assembly.FullName, StringComparer.Ordinal))
|
|
||||||
{
|
|
||||||
yield return associatedAssembly;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual DependencyContext LoadDependencyContext(Assembly assembly) => DependencyContext.Load(assembly);
|
|
||||||
|
|
||||||
private List<AssemblyItem> ResolveFromDependencyContext(DependencyContext dependencyContext)
|
|
||||||
{
|
|
||||||
var assemblyItems = new List<AssemblyItem>();
|
|
||||||
var relatedAssemblies = new Dictionary<Assembly, AssemblyItem>();
|
|
||||||
|
|
||||||
var candidateAssemblies = GetCandidateLibraries(dependencyContext)
|
|
||||||
.SelectMany(library => GetLibraryAssemblies(dependencyContext, library));
|
|
||||||
|
|
||||||
foreach (var assembly in candidateAssemblies)
|
|
||||||
{
|
|
||||||
var assemblyItem = GetAssemblyItem(assembly);
|
|
||||||
assemblyItems.Add(assemblyItem);
|
|
||||||
|
|
||||||
foreach (var relatedAssembly in assemblyItem.RelatedAssemblies)
|
|
||||||
{
|
|
||||||
if (relatedAssemblies.TryGetValue(relatedAssembly, out var otherEntry))
|
|
||||||
{
|
|
||||||
var message = string.Join(
|
|
||||||
Environment.NewLine,
|
|
||||||
Resources.FormatApplicationAssembliesProvider_DuplicateRelatedAssembly(relatedAssembly.FullName),
|
|
||||||
otherEntry.Assembly.FullName,
|
|
||||||
assembly.FullName);
|
|
||||||
|
|
||||||
throw new InvalidOperationException(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
relatedAssemblies.Add(relatedAssembly, assemblyItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove any top level assemblies that appear as an associated assembly.
|
|
||||||
assemblyItems.RemoveAll(item => relatedAssemblies.ContainsKey(item.Assembly));
|
|
||||||
|
|
||||||
return assemblyItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual IEnumerable<Assembly> GetLibraryAssemblies(DependencyContext dependencyContext, RuntimeLibrary runtimeLibrary)
|
|
||||||
{
|
|
||||||
foreach (var assemblyName in runtimeLibrary.GetDefaultAssemblyNames(dependencyContext))
|
|
||||||
{
|
|
||||||
var assembly = Assembly.Load(assemblyName);
|
|
||||||
yield return assembly;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual IReadOnlyList<Assembly> GetRelatedAssemblies(Assembly assembly)
|
|
||||||
{
|
|
||||||
// Do not require related assemblies to be available in the default code path.
|
|
||||||
return RelatedAssemblyAttribute.GetRelatedAssemblies(assembly, throwOnError: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AssemblyItem GetAssemblyItem(Assembly assembly)
|
|
||||||
{
|
|
||||||
var relatedAssemblies = GetRelatedAssemblies(assembly);
|
|
||||||
|
|
||||||
// Ensure we don't have any cycles. A cycle could be formed if a related assembly points to the primary assembly.
|
|
||||||
foreach (var relatedAssembly in relatedAssemblies)
|
|
||||||
{
|
|
||||||
if (relatedAssembly.IsDefined(typeof(RelatedAssemblyAttribute)))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
Resources.FormatApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional(relatedAssembly.FullName, assembly.FullName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AssemblyItem(assembly, relatedAssemblies);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a list of libraries that references the assemblies in <see cref="ReferenceAssemblies"/>.
|
|
||||||
// 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<RuntimeLibrary> GetCandidateLibraries(DependencyContext dependencyContext)
|
|
||||||
{
|
|
||||||
// When using the Microsoft.AspNetCore.App shared runtimes, entries in the RuntimeLibraries
|
|
||||||
// get "erased" and it is no longer accurate to query to determine a library's dependency closure.
|
|
||||||
// We'll use CompileLibraries to calculate the dependency graph and runtime library to resolve assemblies to inspect.
|
|
||||||
var candidatesResolver = new CandidateResolver(dependencyContext.CompileLibraries, ReferenceAssemblies);
|
|
||||||
foreach (var library in dependencyContext.RuntimeLibraries)
|
|
||||||
{
|
|
||||||
if (candidatesResolver.IsCandidate(library))
|
|
||||||
{
|
|
||||||
yield return library;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CandidateResolver
|
|
||||||
{
|
|
||||||
private readonly IDictionary<string, Dependency> _compileLibraries;
|
|
||||||
|
|
||||||
public CandidateResolver(IReadOnlyList<Library> compileLibraries, ISet<string> referenceAssemblies)
|
|
||||||
{
|
|
||||||
var compileLibrariesWithNoDuplicates = new Dictionary<string, Dependency>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (var library in compileLibraries)
|
|
||||||
{
|
|
||||||
if (compileLibrariesWithNoDuplicates.ContainsKey(library.Name))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(Resources.FormatCandidateResolver_DifferentCasedReference(library.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
compileLibrariesWithNoDuplicates.Add(library.Name, CreateDependency(library, referenceAssemblies));
|
|
||||||
}
|
|
||||||
|
|
||||||
_compileLibraries = compileLibrariesWithNoDuplicates;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsCandidate(Library library)
|
|
||||||
{
|
|
||||||
var classification = ComputeClassification(library.Name);
|
|
||||||
return classification == DependencyClassification.ReferencesMvc;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Dependency CreateDependency(Library library, ISet<string> referenceAssemblies)
|
|
||||||
{
|
|
||||||
var classification = DependencyClassification.Unknown;
|
|
||||||
if (referenceAssemblies.Contains(library.Name))
|
|
||||||
{
|
|
||||||
classification = DependencyClassification.MvcReference;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Dependency(library, classification);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DependencyClassification ComputeClassification(string dependency)
|
|
||||||
{
|
|
||||||
if (!_compileLibraries.ContainsKey(dependency))
|
|
||||||
{
|
|
||||||
// Library does not have runtime dependency. Since we can't infer
|
|
||||||
// anything about it's references, we'll assume it does not have a reference to Mvc.
|
|
||||||
return DependencyClassification.DoesNotReferenceMvc;
|
|
||||||
}
|
|
||||||
|
|
||||||
var candidateEntry = _compileLibraries[dependency];
|
|
||||||
if (candidateEntry.Classification != DependencyClassification.Unknown)
|
|
||||||
{
|
|
||||||
return candidateEntry.Classification;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var classification = DependencyClassification.DoesNotReferenceMvc;
|
|
||||||
foreach (var candidateDependency in candidateEntry.Library.Dependencies)
|
|
||||||
{
|
|
||||||
var dependencyClassification = ComputeClassification(candidateDependency.Name);
|
|
||||||
if (dependencyClassification == DependencyClassification.ReferencesMvc ||
|
|
||||||
dependencyClassification == DependencyClassification.MvcReference)
|
|
||||||
{
|
|
||||||
classification = DependencyClassification.ReferencesMvc;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
candidateEntry.Classification = classification;
|
|
||||||
|
|
||||||
return classification;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Dependency
|
|
||||||
{
|
|
||||||
public Dependency(Library library, DependencyClassification classification)
|
|
||||||
{
|
|
||||||
Library = library;
|
|
||||||
Classification = classification;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Library Library { get; }
|
|
||||||
|
|
||||||
public DependencyClassification Classification { get; set; }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"Library: {Library.Name}, Classification: {Classification}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum DependencyClassification
|
|
||||||
{
|
|
||||||
Unknown = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// References (directly or transitively) one of the Mvc packages listed in
|
|
||||||
/// <see cref="ReferenceAssemblies"/>.
|
|
||||||
/// </summary>
|
|
||||||
ReferencesMvc = 1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Does not reference (directly or transitively) one of the Mvc packages listed by
|
|
||||||
/// <see cref="ReferenceAssemblies"/>.
|
|
||||||
/// </summary>
|
|
||||||
DoesNotReferenceMvc = 2,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// One of the references listed in <see cref="ReferenceAssemblies"/>.
|
|
||||||
/// </summary>
|
|
||||||
MvcReference = 3,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly struct AssemblyItem
|
|
||||||
{
|
|
||||||
public AssemblyItem(Assembly assembly, IReadOnlyList<Assembly> associatedAssemblies)
|
|
||||||
{
|
|
||||||
Assembly = assembly;
|
|
||||||
RelatedAssemblies = associatedAssemblies;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Assembly Assembly { get; }
|
|
||||||
|
|
||||||
public IReadOnlyList<Assembly> RelatedAssemblies { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies an assembly to be added as an <see cref="ApplicationPart" />.
|
||||||
|
/// <para>
|
||||||
|
/// In the ordinary case, MVC will generate <see cref="ApplicationPartAttribute" />
|
||||||
|
/// instances on the entry assembly for each dependency that references MVC.
|
||||||
|
/// Each of these assemblies is treated as an <see cref="ApplicationPart" />.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||||
|
public sealed class ApplicationPartAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="ApplicationPartAttribute" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assemblyName">The assembly name.</param>
|
||||||
|
public ApplicationPartAttribute(string assemblyName)
|
||||||
|
{
|
||||||
|
AssemblyName = assemblyName ?? throw new ArgumentNullException(nameof(assemblyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the assembly name.
|
||||||
|
/// </summary>
|
||||||
|
public string AssemblyName { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -52,17 +52,55 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
|
|
||||||
internal void PopulateDefaultParts(string entryAssemblyName)
|
internal void PopulateDefaultParts(string entryAssemblyName)
|
||||||
{
|
{
|
||||||
var entryAssembly = Assembly.Load(new AssemblyName(entryAssemblyName));
|
var assemblies = GetApplicationPartAssemblies(entryAssemblyName);
|
||||||
var assembliesProvider = new ApplicationAssembliesProvider();
|
|
||||||
var applicationAssemblies = assembliesProvider.ResolveAssemblies(entryAssembly);
|
|
||||||
|
|
||||||
foreach (var assembly in applicationAssemblies)
|
var seenAssemblies = new HashSet<Assembly>();
|
||||||
|
|
||||||
|
foreach (var assembly in assemblies)
|
||||||
{
|
{
|
||||||
var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly);
|
if (!seenAssemblies.Add(assembly))
|
||||||
foreach (var part in partFactory.GetApplicationParts(assembly))
|
|
||||||
{
|
{
|
||||||
ApplicationParts.Add(part);
|
// "assemblies" may contain duplicate values, but we want unique ApplicationPart instances.
|
||||||
|
// Note that we prefer using a HashSet over Distinct since the latter isn't
|
||||||
|
// guaranteed to preserve the original ordering.
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly);
|
||||||
|
foreach (var applicationPart in partFactory.GetApplicationParts(assembly))
|
||||||
|
{
|
||||||
|
ApplicationParts.Add(applicationPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<Assembly> GetApplicationPartAssemblies(string entryAssemblyName)
|
||||||
|
{
|
||||||
|
var entryAssembly = Assembly.Load(new AssemblyName(entryAssemblyName));
|
||||||
|
|
||||||
|
// Use ApplicationPartAttribute to get the closure of direct or transitive dependencies
|
||||||
|
// that reference MVC.
|
||||||
|
var assembliesFromAttributes = entryAssembly.GetCustomAttributes<ApplicationPartAttribute>()
|
||||||
|
.Select(name => Assembly.Load(name.AssemblyName))
|
||||||
|
.OrderBy(assembly => assembly.FullName, StringComparer.Ordinal)
|
||||||
|
.SelectMany(GetAsemblyClosure);
|
||||||
|
|
||||||
|
// The SDK will not include the entry assembly as an application part. We'll explicitly list it
|
||||||
|
// and have it appear before all other assemblies \ ApplicationParts.
|
||||||
|
return GetAsemblyClosure(entryAssembly)
|
||||||
|
.Concat(assembliesFromAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<Assembly> GetAsemblyClosure(Assembly assembly)
|
||||||
|
{
|
||||||
|
yield return assembly;
|
||||||
|
|
||||||
|
var relatedAssemblies = RelatedAssemblyAttribute.GetRelatedAssemblies(assembly, throwOnError: false)
|
||||||
|
.OrderBy(assembly => assembly.FullName, StringComparer.Ordinal);
|
||||||
|
|
||||||
|
foreach (var relatedAssembly in relatedAssemblies)
|
||||||
|
{
|
||||||
|
yield return relatedAssembly;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,13 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.Extensions.DependencyModel;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An <see cref="ApplicationPart"/> backed by an <see cref="System.Reflection.Assembly"/>.
|
/// An <see cref="ApplicationPart"/> backed by an <see cref="System.Reflection.Assembly"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AssemblyPart :
|
public class AssemblyPart : ApplicationPart, IApplicationPartTypeProvider
|
||||||
ApplicationPart,
|
|
||||||
IApplicationPartTypeProvider,
|
|
||||||
ICompilationReferencesProvider
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new <see cref="AssemblyPart"/> instance.
|
/// Initializes a new <see cref="AssemblyPart"/> instance.
|
||||||
|
|
@ -39,27 +35,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IEnumerable<TypeInfo> Types => Assembly.DefinedTypes;
|
public IEnumerable<TypeInfo> Types => Assembly.DefinedTypes;
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IEnumerable<string> GetReferencePaths()
|
|
||||||
{
|
|
||||||
if (Assembly.IsDynamic)
|
|
||||||
{
|
|
||||||
// Skip loading process for dynamic assemblies. This prevents DependencyContextLoader from reading the
|
|
||||||
// .deps.json file from either manifest resources or the assembly location, which will fail.
|
|
||||||
return Enumerable.Empty<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
var dependencyContext = DependencyContext.Load(Assembly);
|
|
||||||
if (dependencyContext != null)
|
|
||||||
{
|
|
||||||
return dependencyContext.CompileLibraries.SelectMany(library => library.ResolveReferencePaths());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If an application has been compiled without preserveCompilationContext, return the path to the assembly
|
|
||||||
// as a reference. For runtime compilation, this will allow the compilation to succeed as long as it least
|
|
||||||
// one application part has been compiled with preserveCompilationContext and contains a super set of types
|
|
||||||
// required for the compilation to succeed.
|
|
||||||
return new[] { Assembly.Location };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ Microsoft.AspNetCore.Mvc.RouteAttribute</Description>
|
||||||
<Reference Include="Microsoft.AspNetCore.Routing.Abstractions" />
|
<Reference Include="Microsoft.AspNetCore.Routing.Abstractions" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Routing" />
|
<Reference Include="Microsoft.AspNetCore.Routing" />
|
||||||
<Reference Include="Microsoft.Extensions.DependencyInjection" />
|
<Reference Include="Microsoft.Extensions.DependencyInjection" />
|
||||||
<Reference Include="Microsoft.Extensions.DependencyModel" />
|
|
||||||
<Reference Include="Microsoft.Extensions.FileProviders.Abstractions" />
|
<Reference Include="Microsoft.Extensions.FileProviders.Abstractions" />
|
||||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,544 +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.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.DependencyModel;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
|
||||||
{
|
|
||||||
public class ApplicationAssembliesProviderTest
|
|
||||||
{
|
|
||||||
private static readonly Assembly ThisAssembly = typeof(ApplicationAssembliesProviderTest).Assembly;
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ResolveAssemblies_ReturnsCurrentAssembly_IfNoDepsFileIsPresent()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var provider = new TestApplicationAssembliesProvider();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = provider.ResolveAssemblies(ThisAssembly);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(new[] { ThisAssembly }, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ResolveAssemblies_ReturnsCurrentAssembly_IfDepsFileDoesNotHaveAnyCompileLibraries()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var runtimeLibraries = new[]
|
|
||||||
{
|
|
||||||
GetRuntimeLibrary("MyApp", "Microsoft.AspNetCore.App"),
|
|
||||||
GetRuntimeLibrary("Microsoft.AspNetCore.App", "Microsoft.NETCore.App"),
|
|
||||||
GetRuntimeLibrary("Microsoft.NETCore.App"),
|
|
||||||
GetRuntimeLibrary("ClassLibrary"),
|
|
||||||
};
|
|
||||||
var dependencyContext = GetDependencyContext(compileLibraries: Array.Empty<CompilationLibrary>(), runtimeLibraries);
|
|
||||||
var provider = new TestApplicationAssembliesProvider
|
|
||||||
{
|
|
||||||
DependencyContext = dependencyContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = provider.ResolveAssemblies(ThisAssembly);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(new[] { ThisAssembly }, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ResolveAssemblies_ReturnsRelatedAssembliesOrderedByName()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var assembly1 = typeof(ApplicationAssembliesProvider).Assembly;
|
|
||||||
var assembly2 = typeof(IActionResult).Assembly;
|
|
||||||
var assembly3 = typeof(FactAttribute).Assembly;
|
|
||||||
|
|
||||||
var relatedAssemblies = new[] { assembly1, assembly2, assembly3 };
|
|
||||||
var provider = new TestApplicationAssembliesProvider
|
|
||||||
{
|
|
||||||
GetRelatedAssembliesDelegate = (assembly) => relatedAssemblies,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = provider.ResolveAssemblies(ThisAssembly);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(new[] { ThisAssembly, assembly2, assembly1, assembly3 }, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ResolveAssemblies_ReturnsLibrariesFromTheDepsFileThatReferenceMvc()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mvcAssembly = typeof(IActionResult).Assembly;
|
|
||||||
var classLibrary = typeof(FactAttribute).Assembly;
|
|
||||||
|
|
||||||
var libraries = new Dictionary<string, string[]>
|
|
||||||
{
|
|
||||||
[ThisAssembly.GetName().Name] = new[] { mvcAssembly.GetName().Name, classLibrary.GetName().Name },
|
|
||||||
[mvcAssembly.GetName().Name] = Array.Empty<string>(),
|
|
||||||
[classLibrary.GetName().Name] = new[] { mvcAssembly.GetName().Name },
|
|
||||||
};
|
|
||||||
|
|
||||||
var dependencyContext = GetDependencyContext(libraries);
|
|
||||||
|
|
||||||
var provider = new TestApplicationAssembliesProvider
|
|
||||||
{
|
|
||||||
DependencyContext = dependencyContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = provider.ResolveAssemblies(ThisAssembly);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(new[] { ThisAssembly, classLibrary, }, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ResolveAssemblies_ReturnsRelatedAssembliesForLibrariesFromDepsFile()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mvcAssembly = typeof(IActionResult).Assembly;
|
|
||||||
var classLibrary = typeof(object).Assembly;
|
|
||||||
var relatedPart = typeof(FactAttribute).Assembly;
|
|
||||||
|
|
||||||
var libraries = new Dictionary<string, string[]>
|
|
||||||
{
|
|
||||||
[ThisAssembly.GetName().Name] = new[] { relatedPart.GetName().Name, classLibrary.GetName().Name },
|
|
||||||
[classLibrary.GetName().Name] = new[] { mvcAssembly.GetName().Name },
|
|
||||||
[relatedPart.GetName().Name] = new[] { mvcAssembly.GetName().Name },
|
|
||||||
[mvcAssembly.GetName().Name] = Array.Empty<string>(),
|
|
||||||
};
|
|
||||||
|
|
||||||
var dependencyContext = GetDependencyContext(libraries);
|
|
||||||
|
|
||||||
var provider = new TestApplicationAssembliesProvider
|
|
||||||
{
|
|
||||||
DependencyContext = dependencyContext,
|
|
||||||
GetRelatedAssembliesDelegate = (assembly) =>
|
|
||||||
{
|
|
||||||
if (assembly == classLibrary)
|
|
||||||
{
|
|
||||||
return new[] { relatedPart };
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.Empty<Assembly>();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = provider.ResolveAssemblies(ThisAssembly);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(new[] { ThisAssembly, classLibrary, relatedPart, }, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ResolveAssemblies_ThrowsIfRelatedAssemblyDefinesAdditionalRelatedAssemblies()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expected = $"Assembly 'TestRelatedAssembly' declared as a related assembly by assembly '{ThisAssembly}' cannot define additional related assemblies.";
|
|
||||||
var assembly1 = typeof(ApplicationAssembliesProvider).Assembly;
|
|
||||||
var assembly2 = new TestAssembly();
|
|
||||||
|
|
||||||
var relatedAssemblies = new[] { assembly1, assembly2 };
|
|
||||||
var provider = new TestApplicationAssembliesProvider
|
|
||||||
{
|
|
||||||
GetRelatedAssembliesDelegate = (assembly) => relatedAssemblies,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
var ex = Assert.Throws<InvalidOperationException>(() => provider.ResolveAssemblies(ThisAssembly).ToArray());
|
|
||||||
Assert.Equal(expected, ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ResolveAssemblies_ThrowsIfMultipleAssembliesDeclareTheSameRelatedPart()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mvcAssembly = typeof(IActionResult).Assembly;
|
|
||||||
var libraryAssembly1 = typeof(HttpContext).Assembly;
|
|
||||||
var libraryAssembly2 = typeof(JsonConverter).Assembly;
|
|
||||||
var relatedPart = typeof(FactAttribute).Assembly;
|
|
||||||
var expected = string.Join(
|
|
||||||
Environment.NewLine,
|
|
||||||
$"Each related assembly must be declared by exactly one assembly. The assembly '{relatedPart.FullName}' was declared as related assembly by the following:",
|
|
||||||
libraryAssembly1.FullName,
|
|
||||||
libraryAssembly2.FullName);
|
|
||||||
|
|
||||||
var libraries = new Dictionary<string, string[]>
|
|
||||||
{
|
|
||||||
[ThisAssembly.GetName().Name] = new[] { relatedPart.GetName().Name, libraryAssembly1.GetName().Name },
|
|
||||||
[libraryAssembly1.GetName().Name] = new[] { mvcAssembly.GetName().Name },
|
|
||||||
[libraryAssembly2.GetName().Name] = new[] { mvcAssembly.GetName().Name },
|
|
||||||
[mvcAssembly.GetName().Name] = Array.Empty<string>(),
|
|
||||||
};
|
|
||||||
|
|
||||||
var dependencyContext = GetDependencyContext(libraries);
|
|
||||||
|
|
||||||
var provider = new TestApplicationAssembliesProvider
|
|
||||||
{
|
|
||||||
DependencyContext = dependencyContext,
|
|
||||||
GetRelatedAssembliesDelegate = (assembly) =>
|
|
||||||
{
|
|
||||||
if (assembly == libraryAssembly1 || assembly == libraryAssembly2)
|
|
||||||
{
|
|
||||||
return new[] { relatedPart };
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.Empty<Assembly>();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
var ex = Assert.Throws<InvalidOperationException>(() => provider.ResolveAssemblies(ThisAssembly).ToArray());
|
|
||||||
Assert.Equal(expected, ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void CandidateResolver_ThrowsIfDependencyContextContainsDuplicateRuntimeLibraryNames()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var upperCaseLibrary = "Microsoft.AspNetCore.Mvc";
|
|
||||||
var mixedCaseLibrary = "microsoft.aspNetCore.mvc";
|
|
||||||
|
|
||||||
var libraries = new Dictionary<string, string[]>
|
|
||||||
{
|
|
||||||
[upperCaseLibrary] = Array.Empty<string>(),
|
|
||||||
[mixedCaseLibrary] = Array.Empty<string>(),
|
|
||||||
};
|
|
||||||
var dependencyContext = GetDependencyContext(libraries);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var exception = Assert.Throws<InvalidOperationException>(() => ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext).ToArray());
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal($"A duplicate entry for library reference {mixedCaseLibrary} was found. Please check that all package references in all projects use the same casing for the same package references.", exception.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetCandidateLibraries_IgnoresMvcAssemblies()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expected = GetRuntimeLibrary("SomeRandomAssembly", "Microsoft.AspNetCore.Mvc.Abstractions");
|
|
||||||
var runtimeLibraries = new[]
|
|
||||||
{
|
|
||||||
GetRuntimeLibrary("Microsoft.AspNetCore.Mvc.Core"),
|
|
||||||
GetRuntimeLibrary("Microsoft.AspNetCore.Mvc"),
|
|
||||||
GetRuntimeLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
|
||||||
expected,
|
|
||||||
};
|
|
||||||
|
|
||||||
var compileLibraries = new[]
|
|
||||||
{
|
|
||||||
GetCompileLibrary("Microsoft.AspNetCore.Mvc.Core"),
|
|
||||||
GetCompileLibrary("Microsoft.AspNetCore.Mvc"),
|
|
||||||
GetCompileLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
|
||||||
GetCompileLibrary("SomeRandomAssembly", "Microsoft.AspNetCore.Mvc.Abstractions"),
|
|
||||||
};
|
|
||||||
var dependencyContext = GetDependencyContext(compileLibraries, runtimeLibraries);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(new[] { expected }, candidates);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetCandidateLibraries_ReturnsRuntimeLibraries_IfCompileLibraryDependencyToMvcIsPresent()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
// When an app is running against Microsoft.AspNetCore.App shared runtime or if the DependencyContext is queried
|
|
||||||
// from an app that's running on Microsoft.NETCore.App (e.g. in a unit testing scenario), the
|
|
||||||
// runtime library does not state that the app references Mvc whereas the compile library does. This test validates
|
|
||||||
// that we correctly recognize this scenario.
|
|
||||||
var expected = GetRuntimeLibrary("MyApp", "Microsoft.AspNetCore.App");
|
|
||||||
var runtimeLibraries = new[]
|
|
||||||
{
|
|
||||||
expected,
|
|
||||||
GetRuntimeLibrary("Microsoft.AspNetCore.App", "Microsoft.NETCore.App"),
|
|
||||||
GetRuntimeLibrary("Microsoft.NETCore.App"),
|
|
||||||
};
|
|
||||||
|
|
||||||
var compileLibraries = new[]
|
|
||||||
{
|
|
||||||
GetCompileLibrary("MyApp", "Microsoft.AspNetCore.App"),
|
|
||||||
GetCompileLibrary("Microsoft.AspNetCore.App", "Microsoft.AspNetCore.Mvc", "Microsoft.NETCore.App"),
|
|
||||||
GetCompileLibrary("Microsoft.AspNetCore.Mvc"),
|
|
||||||
GetCompileLibrary("Microsoft.NETCore.App"),
|
|
||||||
};
|
|
||||||
var dependencyContext = GetDependencyContext(compileLibraries, runtimeLibraries);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(new[] { expected }, candidates);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetCandidateLibraries_DoesNotThrow_IfLibraryDoesNotAppearAsCompileOrRuntimeLibrary()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expected = GetRuntimeLibrary("MyApplication", "Microsoft.AspNetCore.Server.Kestrel", "Microsoft.AspNetCore.Mvc");
|
|
||||||
var compileLibraries = new[]
|
|
||||||
{
|
|
||||||
GetCompileLibrary("MyApplication", "Microsoft.AspNetCore.Server.Kestrel", "Microsoft.AspNetCore.Mvc"),
|
|
||||||
GetCompileLibrary("Microsoft.AspNetCore.Mvc"),
|
|
||||||
GetCompileLibrary("Microsoft.AspNetCore.Server.Kestrel", "Libuv"),
|
|
||||||
};
|
|
||||||
|
|
||||||
var runtimeLibraries = new[]
|
|
||||||
{
|
|
||||||
expected,
|
|
||||||
GetRuntimeLibrary("Microsoft.AspNetCore.Server.Kestrel", "Libuv"),
|
|
||||||
GetRuntimeLibrary("Microsoft.AspNetCore.Mvc"),
|
|
||||||
};
|
|
||||||
|
|
||||||
var dependencyContext = GetDependencyContext(compileLibraries, runtimeLibraries);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext).ToList();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(new[] { expected }, candidates);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetCandidateLibraries_DoesNotThrow_IfCompileLibraryIsPresentButNotRuntimeLibrary()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expected = GetRuntimeLibrary("MyApplication", "Microsoft.AspNetCore.Server.Kestrel", "Microsoft.AspNetCore.Mvc");
|
|
||||||
var compileLibraries = new[]
|
|
||||||
{
|
|
||||||
GetCompileLibrary("MyApplication", "Microsoft.AspNetCore.Server.Kestrel", "Microsoft.AspNetCore.Mvc"),
|
|
||||||
GetCompileLibrary("Microsoft.AspNetCore.Mvc"),
|
|
||||||
GetCompileLibrary("Microsoft.AspNetCore.Server.Kestrel", "Libuv"),
|
|
||||||
GetCompileLibrary("Libuv"),
|
|
||||||
};
|
|
||||||
|
|
||||||
var runtimeLibraries = new[]
|
|
||||||
{
|
|
||||||
expected,
|
|
||||||
GetRuntimeLibrary("Microsoft.AspNetCore.Server.Kestrel", "Libuv"),
|
|
||||||
GetRuntimeLibrary("Microsoft.AspNetCore.Mvc"),
|
|
||||||
};
|
|
||||||
|
|
||||||
var dependencyContext = GetDependencyContext(compileLibraries, runtimeLibraries);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext).ToList();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(new[] { expected }, candidates);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetCandidateLibraries_ReturnsLibrariesReferencingAnyMvcAssembly()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var libraries = new Dictionary<string, string[]>
|
|
||||||
{
|
|
||||||
["Foo"] = new[] { "Microsoft.AspNetCore.Mvc.Core" },
|
|
||||||
["Bar"] = new[] { "Microsoft.AspNetCore.Mvc" },
|
|
||||||
["Qux"] = new[] { "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc" },
|
|
||||||
["Baz"] = new[] { "Microsoft.AspNetCore.Mvc.Abstractions" },
|
|
||||||
["Microsoft.AspNetCore.Mvc.Core"] = Array.Empty<string>(),
|
|
||||||
["Microsoft.AspNetCore.Mvc"] = Array.Empty<string>(),
|
|
||||||
["Not.Mvc.Assembly"] = Array.Empty<string>(),
|
|
||||||
["Unofficial.Microsoft.AspNetCore.Mvc"] = Array.Empty<string>(),
|
|
||||||
["Microsoft.AspNetCore.Mvc.Abstractions"] = Array.Empty<string>(),
|
|
||||||
};
|
|
||||||
var dependencyContext = GetDependencyContext(libraries);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(new[] { "Bar", "Baz", "Foo", }, candidates.Select(a => a.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetCandidateLibraries_LibraryNameComparisonsAreCaseInsensitive()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var libraries = new Dictionary<string, string[]>
|
|
||||||
{
|
|
||||||
["Foo"] = new[] { "MICROSOFT.ASPNETCORE.MVC.CORE" },
|
|
||||||
["Bar"] = new[] { "microsoft.aspnetcore.mvc" },
|
|
||||||
["Qux"] = new[] { "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc" },
|
|
||||||
["Baz"] = new[] { "mIcRoSoFt.AsPnEtCoRe.MvC.aBsTrAcTiOnS" },
|
|
||||||
["Microsoft.AspNetCore.Mvc.Core"] = Array.Empty<string>(),
|
|
||||||
["LibraryA"] = new[] { "LIBRARYB" },
|
|
||||||
["LibraryB"] = new[] { "microsoft.aspnetcore.mvc" },
|
|
||||||
["Microsoft.AspNetCore.Mvc"] = Array.Empty<string>(),
|
|
||||||
["Not.Mvc.Assembly"] = Array.Empty<string>(),
|
|
||||||
["Unofficial.Microsoft.AspNetCore.Mvc"] = Array.Empty<string>(),
|
|
||||||
["Microsoft.AspNetCore.Mvc.Abstractions"] = Array.Empty<string>(),
|
|
||||||
};
|
|
||||||
var dependencyContext = GetDependencyContext(libraries);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(new[] { "Bar", "Baz", "Foo", "LibraryA", "LibraryB" }, candidates.Select(a => a.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetCandidateLibraries_ReturnsLibrariesWithTransitiveReferencesToAnyMvcAssembly()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expectedLibraries = new[] { "Bar", "Baz", "Foo", "LibraryA", "LibraryB", "LibraryC", "LibraryE", "LibraryG", "LibraryH" };
|
|
||||||
|
|
||||||
var libraries = new Dictionary<string, string[]>
|
|
||||||
{
|
|
||||||
["Foo"] = new[] { "Bar" },
|
|
||||||
["Bar"] = new[] { "Microsoft.AspNetCore.Mvc" },
|
|
||||||
["Qux"] = new[] { "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc" },
|
|
||||||
["Baz"] = new[] { "Microsoft.AspNetCore.Mvc.Abstractions" },
|
|
||||||
["Microsoft.AspNetCore.Mvc"] = Array.Empty<string>(),
|
|
||||||
["Not.Mvc.Assembly"] = Array.Empty<string>(),
|
|
||||||
["Microsoft.AspNetCore.Mvc.Abstractions"] = Array.Empty<string>(),
|
|
||||||
["Unofficial.Microsoft.AspNetCore.Mvc"] = Array.Empty<string>(),
|
|
||||||
["LibraryA"] = new[] { "LibraryB" },
|
|
||||||
["LibraryB"] = new[] { "LibraryC" },
|
|
||||||
["LibraryC"] = new[] { "LibraryD", "Microsoft.AspNetCore.Mvc.Abstractions" },
|
|
||||||
["LibraryD"] = Array.Empty<string>(),
|
|
||||||
["LibraryE"] = new[] { "LibraryF", "LibraryG" },
|
|
||||||
["LibraryF"] = Array.Empty<string>(),
|
|
||||||
["LibraryG"] = new[] { "LibraryH" },
|
|
||||||
["LibraryH"] = new[] { "LibraryI", "Microsoft.AspNetCore.Mvc" },
|
|
||||||
["LibraryI"] = Array.Empty<string>(),
|
|
||||||
};
|
|
||||||
var dependencyContext = GetDependencyContext(libraries);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expectedLibraries, candidates.Select(a => a.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetCandidateLibraries_SkipsMvcAssemblies()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var libraries = new Dictionary<string, string[]>
|
|
||||||
{
|
|
||||||
["MvcSandbox"] = new[] { "Microsoft.AspNetCore.Mvc.Core", "Microsoft.AspNetCore.Mvc" },
|
|
||||||
["Microsoft.AspNetCore.Mvc.Core"] = new[] { "Microsoft.AspNetCore.HttpAbstractions" },
|
|
||||||
["Microsoft.AspNetCore.HttpAbstractions"] = Array.Empty<string>(),
|
|
||||||
["Microsoft.AspNetCore.Mvc"] = new[] { "Microsoft.AspNetCore.Mvc.Abstractions", "Microsoft.AspNetCore.Mvc.Core" },
|
|
||||||
["Microsoft.AspNetCore.Mvc.Abstractions"] = Array.Empty<string>(),
|
|
||||||
["Microsoft.AspNetCore.Mvc.TagHelpers"] = new[] { "Microsoft.AspNetCore.Mvc.Razor" },
|
|
||||||
["Microsoft.AspNetCore.Mvc.Razor"] = Array.Empty<string>(),
|
|
||||||
["ControllersAssembly"] = new[] { "Microsoft.AspNetCore.Mvc" },
|
|
||||||
};
|
|
||||||
|
|
||||||
var dependencyContext = GetDependencyContext(libraries);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(new[] { "ControllersAssembly", "MvcSandbox" }, candidates.Select(a => a.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestApplicationAssembliesProvider : ApplicationAssembliesProvider
|
|
||||||
{
|
|
||||||
public DependencyContext DependencyContext { get; set; }
|
|
||||||
|
|
||||||
public Func<Assembly, IReadOnlyList<Assembly>> GetRelatedAssembliesDelegate { get; set; } = (assembly) => Array.Empty<Assembly>();
|
|
||||||
|
|
||||||
protected override DependencyContext LoadDependencyContext(Assembly assembly) => DependencyContext;
|
|
||||||
|
|
||||||
protected override IReadOnlyList<Assembly> GetRelatedAssemblies(Assembly assembly) => GetRelatedAssembliesDelegate(assembly);
|
|
||||||
|
|
||||||
protected override IEnumerable<Assembly> GetLibraryAssemblies(DependencyContext dependencyContext, RuntimeLibrary runtimeLibrary)
|
|
||||||
{
|
|
||||||
var assemblyName = new AssemblyName(runtimeLibrary.Name);
|
|
||||||
yield return Assembly.Load(assemblyName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DependencyContext GetDependencyContext(IDictionary<string, string[]> libraries)
|
|
||||||
{
|
|
||||||
var compileLibraries = new List<CompilationLibrary>();
|
|
||||||
var runtimeLibraries = new List<RuntimeLibrary>();
|
|
||||||
|
|
||||||
foreach (var kvp in libraries.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))
|
|
||||||
{
|
|
||||||
var compileLibrary = GetCompileLibrary(kvp.Key, kvp.Value);
|
|
||||||
compileLibraries.Add(compileLibrary);
|
|
||||||
|
|
||||||
var runtimeLibrary = GetRuntimeLibrary(kvp.Key, kvp.Value);
|
|
||||||
runtimeLibraries.Add(runtimeLibrary);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetDependencyContext(compileLibraries, runtimeLibraries);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DependencyContext GetDependencyContext(
|
|
||||||
IReadOnlyList<CompilationLibrary> compileLibraries,
|
|
||||||
IReadOnlyList<RuntimeLibrary> runtimeLibraries)
|
|
||||||
{
|
|
||||||
var dependencyContext = new DependencyContext(
|
|
||||||
new TargetInfo("framework", "runtime", "signature", isPortable: true),
|
|
||||||
CompilationOptions.Default,
|
|
||||||
compileLibraries,
|
|
||||||
runtimeLibraries,
|
|
||||||
Enumerable.Empty<RuntimeFallbacks>());
|
|
||||||
return dependencyContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RuntimeLibrary GetRuntimeLibrary(string name, params string[] dependencyNames)
|
|
||||||
{
|
|
||||||
var dependencies = dependencyNames?.Select(d => new Dependency(d, "42.0.0")) ?? new Dependency[0];
|
|
||||||
|
|
||||||
return new RuntimeLibrary(
|
|
||||||
"package",
|
|
||||||
name,
|
|
||||||
"23.0.0",
|
|
||||||
"hash",
|
|
||||||
new RuntimeAssetGroup[0],
|
|
||||||
new RuntimeAssetGroup[0],
|
|
||||||
new ResourceAssembly[0],
|
|
||||||
dependencies: dependencies.ToArray(),
|
|
||||||
serviceable: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static CompilationLibrary GetCompileLibrary(string name, params string[] dependencyNames)
|
|
||||||
{
|
|
||||||
var dependencies = dependencyNames?.Select(d => new Dependency(d, "42.0.0")) ?? new Dependency[0];
|
|
||||||
|
|
||||||
return new CompilationLibrary(
|
|
||||||
"package",
|
|
||||||
name,
|
|
||||||
"23.0.0",
|
|
||||||
"hash",
|
|
||||||
Enumerable.Empty<string>(),
|
|
||||||
dependencies: dependencies.ToArray(),
|
|
||||||
serviceable: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestAssembly : Assembly
|
|
||||||
{
|
|
||||||
public override string FullName => "TestRelatedAssembly";
|
|
||||||
|
|
||||||
public override bool IsDefined(Type attributeType, bool inherit)
|
|
||||||
{
|
|
||||||
return attributeType == typeof(RelatedAssemblyAttribute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -50,55 +50,5 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
Assert.Equal(part.Assembly, assembly);
|
Assert.Equal(part.Assembly, assembly);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetReferencePaths_ReturnsReferencesFromDependencyContext_IfPreserveCompilationContextIsSet()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var assembly = GetType().GetTypeInfo().Assembly;
|
|
||||||
var part = new AssemblyPart(assembly);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var references = part.GetReferencePaths().ToList();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Contains(assembly.Location, references);
|
|
||||||
Assert.Contains(
|
|
||||||
typeof(AssemblyPart).GetTypeInfo().Assembly.GetName().Name,
|
|
||||||
references.Select(Path.GetFileNameWithoutExtension));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetReferencePaths_ReturnsAssemblyLocation_IfPreserveCompilationContextIsNotSet()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
// src projects do not have preserveCompilationContext specified.
|
|
||||||
var assembly = typeof(AssemblyPart).GetTypeInfo().Assembly;
|
|
||||||
var part = new AssemblyPart(assembly);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var references = part.GetReferencePaths().ToList();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var actual = Assert.Single(references);
|
|
||||||
Assert.Equal(assembly.Location, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetReferencePaths_ReturnsEmptySequenceForDynamicAssembly()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var name = new AssemblyName($"DynamicAssembly-{Guid.NewGuid()}");
|
|
||||||
var assembly = AssemblyBuilder.DefineDynamicAssembly(name,
|
|
||||||
AssemblyBuilderAccess.RunAndCollect);
|
|
||||||
|
|
||||||
var part = new AssemblyPart(assembly);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var references = part.GetReferencePaths().ToList();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Empty(references);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,6 @@
|
||||||
<Reference Include="Microsoft.AspNetCore.Mvc.RazorPages" />
|
<Reference Include="Microsoft.AspNetCore.Mvc.RazorPages" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Razor.Runtime" />
|
<Reference Include="Microsoft.AspNetCore.Razor.Runtime" />
|
||||||
<Reference Include="Microsoft.CodeAnalysis.Razor" />
|
<Reference Include="Microsoft.CodeAnalysis.Razor" />
|
||||||
|
<Reference Include="Microsoft.Extensions.DependencyModel" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
|
{
|
||||||
|
public static partial class AssemblyPartExtensions
|
||||||
|
{
|
||||||
|
public static System.Collections.Generic.IEnumerable<string> GetReferencePaths(this Microsoft.AspNetCore.Mvc.ApplicationParts.AssemblyPart assemblyPart) { throw null; }
|
||||||
|
}
|
||||||
|
}
|
||||||
namespace Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation
|
namespace Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation
|
||||||
{
|
{
|
||||||
public partial class FileProviderRazorProjectItem : Microsoft.AspNetCore.Razor.Language.RazorProjectItem
|
public partial class FileProviderRazorProjectItem : Microsoft.AspNetCore.Razor.Language.RazorProjectItem
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
// 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 Microsoft.Extensions.DependencyModel;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
|
{
|
||||||
|
public static class AssemblyPartExtensions
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public static IEnumerable<string> GetReferencePaths(this AssemblyPart assemblyPart)
|
||||||
|
{
|
||||||
|
var assembly = assemblyPart?.Assembly ?? throw new ArgumentNullException(nameof(assemblyPart));
|
||||||
|
|
||||||
|
if (assembly.IsDynamic)
|
||||||
|
{
|
||||||
|
// Skip loading process for dynamic assemblies. This prevents DependencyContextLoader from reading the
|
||||||
|
// .deps.json file from either manifest resources or the assembly location, which will fail.
|
||||||
|
return Enumerable.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var dependencyContext = DependencyContext.Load(assembly);
|
||||||
|
if (dependencyContext != null)
|
||||||
|
{
|
||||||
|
return dependencyContext.CompileLibraries.SelectMany(library => library.ResolveReferencePaths());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an application has been compiled without preserveCompilationContext, return the path to the assembly
|
||||||
|
// as a reference. For runtime compilation, this will allow the compilation to succeed as long as it least
|
||||||
|
// one application part has been compiled with preserveCompilationContext and contains a super set of types
|
||||||
|
// required for the compilation to succeed.
|
||||||
|
return new[] { assembly.Location };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
<Reference Include="Microsoft.AspNetCore.Mvc.RazorPages" />
|
<Reference Include="Microsoft.AspNetCore.Mvc.RazorPages" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Razor.Runtime" />
|
<Reference Include="Microsoft.AspNetCore.Razor.Runtime" />
|
||||||
<Reference Include="Microsoft.CodeAnalysis.Razor" />
|
<Reference Include="Microsoft.CodeAnalysis.Razor" />
|
||||||
|
<Reference Include="Microsoft.Extensions.DependencyModel" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// 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.
|
// 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.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
@ -53,14 +52,21 @@ namespace Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation
|
||||||
// For unit testing
|
// For unit testing
|
||||||
internal IEnumerable<string> GetReferencePaths()
|
internal IEnumerable<string> GetReferencePaths()
|
||||||
{
|
{
|
||||||
var referencesFromApplicationParts = _partManager
|
var referencePaths = new List<string>();
|
||||||
.ApplicationParts
|
|
||||||
.OfType<ICompilationReferencesProvider>()
|
|
||||||
.SelectMany(part => part.GetReferencePaths());
|
|
||||||
|
|
||||||
var referencePaths = referencesFromApplicationParts
|
foreach (var part in _partManager.ApplicationParts)
|
||||||
.Concat(_options.AdditionalReferencePaths)
|
{
|
||||||
.Distinct(StringComparer.OrdinalIgnoreCase);
|
if (part is ICompilationReferencesProvider compilationReferenceProvider)
|
||||||
|
{
|
||||||
|
referencePaths.AddRange(compilationReferenceProvider.GetReferencePaths());
|
||||||
|
}
|
||||||
|
else if (part is AssemblyPart assemblyPart)
|
||||||
|
{
|
||||||
|
referencePaths.AddRange(assemblyPart.GetReferencePaths());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
referencePaths.AddRange(_options.AdditionalReferencePaths);
|
||||||
|
|
||||||
return referencePaths;
|
return referencePaths;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
// 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.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
|
{
|
||||||
|
public class AssemblyPartExtensionTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void GetReferencePaths_ReturnsReferencesFromDependencyContext_IfPreserveCompilationContextIsSet()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var assembly = GetType().GetTypeInfo().Assembly;
|
||||||
|
var part = new AssemblyPart(assembly);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var references = part.GetReferencePaths().ToList();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Contains(assembly.Location, references);
|
||||||
|
Assert.Contains(
|
||||||
|
typeof(AssemblyPart).GetTypeInfo().Assembly.GetName().Name,
|
||||||
|
references.Select(Path.GetFileNameWithoutExtension));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetReferencePaths_ReturnsAssemblyLocation_IfPreserveCompilationContextIsNotSet()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
// src projects do not have preserveCompilationContext specified.
|
||||||
|
var assembly = typeof(AssemblyPart).GetTypeInfo().Assembly;
|
||||||
|
var part = new AssemblyPart(assembly);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var references = part.GetReferencePaths().ToList();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var actual = Assert.Single(references);
|
||||||
|
Assert.Equal(assembly.Location, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetReferencePaths_ReturnsEmptySequenceForDynamicAssembly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var name = new AssemblyName($"DynamicAssembly-{Guid.NewGuid()}");
|
||||||
|
var assembly = AssemblyBuilder.DefineDynamicAssembly(name,
|
||||||
|
AssemblyBuilderAccess.RunAndCollect);
|
||||||
|
|
||||||
|
var part = new AssemblyPart(assembly);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var references = part.GetReferencePaths().ToList();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(references);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
<Reference Include="Microsoft.AspNetCore.TestHost" />
|
<Reference Include="Microsoft.AspNetCore.TestHost" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Mvc.Core" />
|
<Reference Include="Microsoft.AspNetCore.Mvc.Core" />
|
||||||
<Reference Include="Microsoft.Extensions.HostFactoryResolver.Sources" />
|
<Reference Include="Microsoft.Extensions.HostFactoryResolver.Sources" />
|
||||||
|
<Reference Include="Microsoft.Extensions.DependencyModel" />
|
||||||
<Reference Include="Microsoft.Extensions.Hosting" />
|
<Reference Include="Microsoft.Extensions.Hosting" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
<Reference Include="Microsoft.AspNetCore.TestHost" />
|
<Reference Include="Microsoft.AspNetCore.TestHost" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Mvc.Core" />
|
<Reference Include="Microsoft.AspNetCore.Mvc.Core" />
|
||||||
<Reference Include="Microsoft.Extensions.HostFactoryResolver.Sources" />
|
<Reference Include="Microsoft.Extensions.HostFactoryResolver.Sources" />
|
||||||
|
<Reference Include="Microsoft.Extensions.DependencyModel" />
|
||||||
<Reference Include="Microsoft.Extensions.Hosting" />
|
<Reference Include="Microsoft.Extensions.Hosting" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,58 +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.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using Microsoft.Extensions.DependencyModel;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
|
||||||
{
|
|
||||||
public class ApplicationAssembliesProviderTest
|
|
||||||
{
|
|
||||||
private static readonly Assembly ThisAssembly = typeof(ApplicationAssembliesProviderTest).Assembly;
|
|
||||||
|
|
||||||
// This test verifies ApplicationAssembliesProviderTest.ReferenceAssemblies reflects the actual loadable assemblies
|
|
||||||
// of the libraries that Microsoft.AspNetCore.Mvc depends on.
|
|
||||||
// If we add or remove dependencies, this test should be changed together.
|
|
||||||
[Fact]
|
|
||||||
public void ReferenceAssemblies_ReturnsLoadableReferenceAssemblies()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var excludeAssemblies = new string[]
|
|
||||||
{
|
|
||||||
"Microsoft.AspNetCore.Mvc.Analyzers",
|
|
||||||
"Microsoft.AspNetCore.Mvc.Test",
|
|
||||||
"Microsoft.AspNetCore.Mvc.Core.TestCommon",
|
|
||||||
};
|
|
||||||
|
|
||||||
var additionalAssemblies = new[]
|
|
||||||
{
|
|
||||||
// The following assemblies are not reachable from Microsoft.AspNetCore.Mvc
|
|
||||||
"Microsoft.AspNetCore.App",
|
|
||||||
"Microsoft.AspNetCore.Mvc.NewtonsoftJson",
|
|
||||||
"Microsoft.AspNetCore.Mvc.Formatters.Xml",
|
|
||||||
};
|
|
||||||
|
|
||||||
var dependencyContextLibraries = DependencyContext.Load(ThisAssembly)
|
|
||||||
.CompileLibraries
|
|
||||||
.Where(r => r.Name.StartsWith("Microsoft.AspNetCore.Mvc", StringComparison.OrdinalIgnoreCase) &&
|
|
||||||
!excludeAssemblies.Contains(r.Name, StringComparer.OrdinalIgnoreCase))
|
|
||||||
.Select(r => r.Name);
|
|
||||||
|
|
||||||
var expected = dependencyContextLibraries
|
|
||||||
.Concat(additionalAssemblies)
|
|
||||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
|
||||||
.OrderBy(p => p, StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var referenceAssemblies = ApplicationAssembliesProvider
|
|
||||||
.ReferenceAssemblies
|
|
||||||
.OrderBy(p => p, StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expected, referenceAssemblies, StringComparer.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// This will eventually be codegened, but it's fine for duplicates to be present
|
||||||
|
[assembly: ApplicationPart("RazorPagesClassLibrary")]
|
||||||
Loading…
Reference in New Issue