net core手动加载dll,无法自动加载其依赖项
用的net core版本是2.1,也许在后续的版本中已经修复了这个问题
今天在尝试用net core写demo的时候,发现了这个问题。因为都是使用DI,所以就没有我的网站项目里直接引用一些实现类库,而是放到了同一个目录下,在网站启动的时候用代码去加载进来。然而在实际的运行过程成中发现,指定的dll会自动加载,但是其依赖的nuget包里的dll不会被加载进来,在Google了很久,也发现了很多人提出过这个问题,在GitHub上也有人提过https://github.com/dotnet/corefx/issues/21982,但是都没有直接的解决方案,其中有一个差不多的解决方案https://www.codeproject.com/Articles/1194332/Resolving-Assemblies-in-NET-Core,我的解决方案也是依据这个改进而来的。
代码的核心思路是去找需要手动加载的DLL的依赖项,尝试去找到该依赖项所在的位置,然后再加载进来。详细代码如下:
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyModel; using Microsoft.Extensions.DependencyModel.Resolution; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.Loader; using ZRB.Blog.Configurations; namespace ZRB.Blog.Injection { public static class AssemblyLoader { private static readonly ICompilationAssemblyResolver AssemblyResolver; private static readonly ConcurrentDictionary<string, CompilationLibrary> DependencyDLL; static AssemblyLoader() { AssemblyLoadContext.Default.Resolving += Default_Resolving; AssemblyResolver = new CompositeCompilationAssemblyResolver( new ICompilationAssemblyResolver[]{ new AppBaseCompilationAssemblyResolver(AppDomain.CurrentDomain.BaseDirectory), new ReferenceAssemblyPathResolver(), new PackageCompilationAssemblyResolver() }); DependencyDLL = new ConcurrentDictionary<string, CompilationLibrary>(); } private static Assembly Default_Resolving(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName) { if(DependencyDLL.ContainsKey(assemblyName.Name)) { var compilationLibrary = DependencyDLL[assemblyName.Name]; var assemblies = new List<string>(); if (AssemblyResolver.TryResolveAssemblyPaths(compilationLibrary, assemblies) && assemblies.Count > 0) { var assembly = assemblyLoadContext.LoadFromAssemblyPath(assemblies[0]); FindDependency(assembly); return assembly; } } return null; } public static Assembly GetAssembly(string assemblyName) { string assemblyFileName = AppDomain.CurrentDomain.BaseDirectory + assemblyName + ".dll"; Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName?.Split(',')[0] == assemblyName) ?? AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyFileName); FindDependency(assembly); return assembly; } private static void FindDependency(Assembly assembly) { DependencyContext dependencyContext = DependencyContext.Load(assembly); if(dependencyContext!= null) { foreach (var compilationLibrary in dependencyContext.CompileLibraries) { if (!DependencyDLL.ContainsKey(compilationLibrary.Name) && !AppDomain.CurrentDomain.GetAssemblies().Any(a => a.FullName.Split(',')[0] == compilationLibrary.Name)) { RuntimeLibrary library = dependencyContext.RuntimeLibraries.FirstOrDefault(runtime => runtime.Name == compilationLibrary.Name); var cb = new CompilationLibrary( library.Type, library.Name, library.Version, library.Hash, library.RuntimeAssemblyGroups.SelectMany(g => g.AssetPaths), library.Dependencies, library.Serviceable); DependencyDLL[library.Name] = cb; } } } } } }