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