.Net Core利用反射動態載入類庫的方法(解決類庫不包含Nuget依賴包的問題)

iDream2016發表於2021-09-28

  在.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

 

 

二、類庫編譯生成多餘的dll

 

  對於類庫專案來說,通常會引用解決方案中其他通用專案,而主體程式也會引用這些通用專案,所以對於類庫來說,在編譯生成的檔案中,並不需要這些檔案。這種情況下,也需要修改.csproj專案檔案,如下圖,生成的類庫檔案將不會包含pluginBase.csproj類庫及其所有的依賴;

 

 

 

三、利用反射動態載入類庫

  如果按照文末參考文獻中的教程,我嘗試過仍會出現找不到“某某.dll”的問題,這邊貼出我的程式碼,供參考:

.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 }
View Code

其中Context_Resolving(),是用於載入類庫檔案過程中,處理觸發載入相關依賴檔案的事件的方法,通過上述程式碼,可以保證將類庫的完整地動態載入程式序,並且不會與其他動態載入類庫專案發生程式集衝突的問題:比如A類庫和B類庫都有共同的依賴C,但兩者的引用的C版本不同,通過AssemblyLoadContext可以保證A/B類庫載入自己需要的版本。

四、結尾

  在.Net Core動態載入類庫還是挺多坑的,以上是我踩過來,與大家分享~

 

參考文獻:

1、使用外掛建立 .NET Core 應用程式

 

相關文章