.Net Core利用反射动态加载类库的方法(解决类库不包含Nuget依赖包的问题)

时间:2023-03-09 21:42:58
.Net Core利用反射动态加载类库的方法(解决类库不包含Nuget依赖包的问题)

  在.Net Framework时代,生成类库只需将类库项目编译好,然后拷贝到其他项目,即可引用或动态加载,相对来说,比较简单。但到了.Net Core时代,动态加载第三方类库,则稍微麻烦一些。

一、类库发布丢失Nuget依赖包

   对于大部分类库来说,项目或多或少会引用第三方程序集,特别是Nuget程序包。通常编译类库项目生成的文件中,并不会包含引用的Nuget包相关类库,而是通过*.deps.json文件来描述类库所需的依赖。这样造成一种问题,如果该类库是通过动态加载的方式引用,则程序运行时,会提示“缺少某某dll”的问题。

  解决上述问题,需要在该类库项目的.csproj文件中,在<PropertyGroup></PropertyGroup>标签中加入<EnableDynamicLoading>true</EnableDynamicLoading>标志,该属性将告诉编译器,该项目是动态加载的组件。相关链接:https://docs.microsoft.com/zh-cn/dotnet/core/project-sdk/msbuild-props#enabledynamicloading

.Net Core利用反射动态加载类库的方法(解决类库不包含Nuget依赖包的问题)

二、类库编译生成多余的dll

  对于类库项目来说,通常会引用解决方案中其他通用项目,而主体程序也会引用这些通用项目,所以对于类库来说,在编译生成的文件中,并不需要这些文件。这种情况下,也需要修改.csproj项目文件,如下图,生成的类库文件将不会包含pluginBase.csproj类库及其所有的依赖;

.Net Core利用反射动态加载类库的方法(解决类库不包含Nuget依赖包的问题)

三、利用反射动态加载类库

  如果按照文末参考文献中的教程,我尝试过仍会出现找不到“某某.dll”的问题,这边贴出我的代码,供参考:

.Net Core利用反射动态加载类库的方法(解决类库不包含Nuget依赖包的问题).Net Core利用反射动态加载类库的方法(解决类库不包含Nuget依赖包的问题)
 1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Reflection;
5 using System.Runtime.Loader;
6
7 namespace LoadDLL
8 {
9 /// <summary>
10 /// 程序集加载器
11 /// </summary>
12 public class AssemblyLoader
13 {
14 private string _basePath;
15 private AssemblyLoadContext context;
16
17
18 public AssemblyLoader(string basePath)
19 {
20 _basePath = basePath;
21 }
22
23 public Type Load(string dllFileName, string typeName)
24 {
25 context = new AssemblyLoadContext(dllFileName);
26 context.Resolving += Context_Resolving;
27 //需要绝对路径
28 string path = Path.Combine(_basePath, dllFileName);
29 if (File.Exists(path))
30 {
31 try
32 {
33 using (var stream = File.OpenRead(path))
34 {
35 Assembly assembly = context.LoadFromStream(stream);
36 Type type = assembly.GetType(typeName);
37 dicTypes.Add(typeName, type);
38 return type;
39 }
40 }
41 catch (Exception ex)
42 {
43 Console.WriteLine($"加载节点{dllFileName}-{typeName}发生异常:{ex.Message},{ex.StackTrace}");
44 }
45
46 }
47 else
48 {
49 Console.WriteLine($"节点动态库{dllFileName}不存在:{path}");
50 }
51 return null;
52 }
53
54 /// <summary>
55 /// 加载依赖文件
56 /// </summary>
57 /// <param name="context"></param>
58 /// <param name="assemblyName"></param>
59 /// <returns></returns>
60 private Assembly Context_Resolving(AssemblyLoadContext context, AssemblyName assemblyName)
61 {
62 string expectedPath = Path.Combine(_basePath, assemblyName.Name + ".dll"); ;
63 if (File.Exists(expectedPath))
64 {
65 try
66 {
67 using (var stream = File.OpenRead(expectedPath))
68 {
69 return context.LoadFromStream(stream);
70 }
71 }
72 catch (Exception ex)
73 {
74 Console.WriteLine($"加载节点{expectedPath}发生异常:{ex.Message},{ex.StackTrace}");
75 }
76 }
77 else
78 {
79 Console.WriteLine($"依赖文件不存在:{expectedPath}");
80 }
81 return null;
82 }
83 }
84 }

其中Context_Resolving(),是用于加载类库文件过程中,处理触发加载相关依赖文件的事件的方法,通过上述代码,可以保证将类库的完整地动态加载进程序,并且不会与其他动态加载类库项目发生程序集冲突的问题:比如A类库和B类库都有共同的依赖C,但两者的引用的C版本不同,通过AssemblyLoadContext可以保证A/B类库加载自己需要的版本。

四、结尾

  在.Net Core动态加载类库还是挺多坑的,以上是我踩过来,与大家分享~

参考文献:

1、使用插件创建 .NET Core 应用程序