// 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.IO; using System.Linq; using System.Reflection; using Microsoft.AspNetCore.Mvc.Core; namespace Microsoft.AspNetCore.Mvc.ApplicationParts { /// /// Specifies a assembly to load as part of MVC's assembly discovery mechanism. /// [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] public sealed class RelatedAssemblyAttribute : Attribute { private static readonly Func AssemblyLoadFileDelegate = Assembly.LoadFile; /// /// Initializes a new instance of . /// /// The file name, without extension, of the related assembly. public RelatedAssemblyAttribute(string assemblyFileName) { if (string.IsNullOrEmpty(assemblyFileName)) { throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(assemblyFileName)); } AssemblyFileName = assemblyFileName; } /// /// Gets the assembly file name without extension. /// public string AssemblyFileName { get; } /// /// Gets instances specified by . /// /// The assembly containing instances. /// Determines if the method throws if a related assembly could not be located. /// Related instances. public static IReadOnlyList GetRelatedAssemblies(Assembly assembly, bool throwOnError) { if (assembly == null) { throw new ArgumentNullException(nameof(assembly)); } return GetRelatedAssemblies(assembly, throwOnError, AssemblyLoadFileDelegate); } internal static IReadOnlyList GetRelatedAssemblies(Assembly assembly, bool throwOnError, Func loadFile) { if (assembly == null) { throw new ArgumentNullException(nameof(assembly)); } // MVC will specifically look for related parts in the same physical directory as the assembly. // No-op if the assembly does not have a location. if (assembly.IsDynamic || string.IsNullOrEmpty(assembly.CodeBase)) { return Array.Empty(); } var attributes = assembly.GetCustomAttributes().ToArray(); if (attributes.Length == 0) { return Array.Empty(); } var assemblyName = assembly.GetName().Name; var assemblyDirectory = Path.GetDirectoryName(assembly.CodeBase); var relatedAssemblies = new List(); for (var i = 0; i < attributes.Length; i++) { var attribute = attributes[i]; if (string.Equals(assemblyName, attribute.AssemblyFileName, StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException( Resources.FormatRelatedAssemblyAttribute_AssemblyCannotReferenceSelf(nameof(RelatedAssemblyAttribute), assemblyName)); } var relatedAssemblyLocation = Path.Combine(assemblyDirectory, attribute.AssemblyFileName + ".dll"); if (!File.Exists(relatedAssemblyLocation)) { if (throwOnError) { throw new FileNotFoundException( Resources.FormatRelatedAssemblyAttribute_CouldNotBeFound(attribute.AssemblyFileName, assemblyName, assemblyDirectory), relatedAssemblyLocation); } else { continue; } } var relatedAssembly = loadFile(relatedAssemblyLocation); relatedAssemblies.Add(relatedAssembly); } return relatedAssemblies; } } }