using System; using System.Collections.Generic; using System.IO; namespace Core.Common.Assembly { /// /// Static class that tries to resolve any missing assemblies from the directory that /// is provided during initialization. The contents of the assembly directory will be /// iterated recursively up until the point where one or more assemblies are found. /// /// When multiple assemblies are found with the same name, only the last one /// will actually be resolved at runtime. /// /// Given the following assembly directory structure: /// /// /// /// Directory /// /// /// Directory /// /// Assembly_1.dll /// /// b /// /// /// /// Directory /// /// Assembly_2.dll /// /// /// Assembly_3.dll * /// /// /// /// /// /// Directory /// /// Assembly_4.dll /// /// /// /// Directory /// /// Assembly_5.dll /// /// /// /// /// /// /// /// Directory /// /// Assembly_3.dll ** /// /// /// Assembly_6.dll /// /// /// /// /// /// /// Then the following assemblies will be resolved: /// /// /// Assembly_1.dll /// /// /// Assembly_2.dll /// /// /// Assembly_3.dll ** /// /// /// Assembly_4.dll /// /// /// Assembly_6.dll /// /// /// public static class AssemblyResolver { private const string assemblySearchPattern = "*.dll"; private static Dictionary assemblyLookup = new Dictionary(); private static bool initialized; /// /// Gets whether or not requires initialization. /// public static bool RequiresInitialization => !initialized; /// /// Initializes the . /// /// The directory to recursively resolve any missing assemblies from. /// Thrown when the /// is already initialized. /// Thrown when cannot /// be found. /// /// After successfully performing this method, equals false. /// public static void Initialize(string assemblyDirectory) { if (initialized) { throw new InvalidOperationException("Cannot initialize the assembly resolver more than once."); } if (!Directory.Exists(assemblyDirectory)) { throw new DirectoryNotFoundException($"Cannot find the directory '{assemblyDirectory}'."); } initialized = true; InitializeAssemblyLookup(assemblyDirectory); AppDomain.CurrentDomain.AssemblyResolve += LoadFileFromAssemblyLookup; } /// /// Resets the . /// /// /// After performing this method, equals true. /// public static void Reset() { initialized = false; assemblyLookup = new Dictionary(); AppDomain.CurrentDomain.AssemblyResolve -= LoadFileFromAssemblyLookup; } private static void InitializeAssemblyLookup(string assemblyDirectory) { string[] assemblies = Directory.GetFiles(assemblyDirectory, assemblySearchPattern); foreach (string directory in Directory.GetDirectories(assemblyDirectory)) { InitializeAssemblyLookup(directory); } foreach (string assembly in assemblies) { assemblyLookup[Path.GetFileNameWithoutExtension(assembly)] = assembly; } } private static System.Reflection.Assembly LoadFileFromAssemblyLookup(object sender, ResolveEventArgs args) { string assemblyName = args.Name.Substring(0, args.Name.IndexOf(',')); if (assemblyLookup.TryGetValue(assemblyName, out string assemblyFile)) { return System.Reflection.Assembly.LoadFile(assemblyFile); } return null; } } }