(原創)[C#] MEF 主程式與外掛載入不同版本的DLL

leslie_xin發表於2022-05-19

一、前言

MEF(Managed Extensibility Framework),是輕量級的外掛框架。使用簡單,功能強大。詳細介紹見MSDN,本文不再贅述。
在使用MEF時,會遇到這樣一種場景:
主程式和外掛都引用了同一個DLL中同一個【方法F】,但是引用的DLL版本不一致。

那麼,程式在執行時,會出現4種情況:
(注:文字描述不太直觀,可參照下節的實際演示)
1,不同版本DLL中【方法F】未做改變:外掛可正常呼叫【方法F】。
2,不同版本DLL中【方法F】內部實現做了改變:引用了與主程式所引用的DLL版本不一致的外掛,在呼叫【方法F】時,呼叫的不是外掛所引用的DLL版本中的【方法F】,而是呼叫的主程式所引用的DLL版本中的【方法F】。
3,不同版本DLL中【方法F】增加了過載方法:如果主程式所引用的DLL版本是包含過載方法的(即主程式所引用的DLL版本比外掛引用的DLL版本),那麼外掛都可以正常呼叫,不過呼叫的【方法F】來自主程式所引用的DLL版本中的【方法F】;相反,如果主程式所引用的DLL版本是未包含過載方法的(即主程式所引用的DLL版本比外掛引用的DLL版本),那麼,那些呼叫了【過載方法F】的外掛在執行時將會報錯。
4,不同版本DLL中【方法F】發生了改變——增減引數、改變返回值型別、刪除了方法等:引用了與主程式所引用的DLL版本不一致的外掛,在呼叫【方法F】時會報錯。

本篇文章,就是來講一下如何實現主程式與外掛們各自呼叫各自版本的DLL,互不影響且正常呼叫的。

相信看完的你,一定會有所收穫!

本文地址:https://www.cnblogs.com/lesliexin/p/16280161.html


二、問題復現

(一)程式碼結構

為了方便演示,我們建立一個簡單的MEF程式,其結構如下:

image

其中:

1,介面

介面類很簡單,包含一個介面定義和一個自定義的MEF匯出特性標籤。

a,介面定義中,只包含了一個方法:Run();

image

b,自定義的MEF匯出特性標籤,主要是為MEF外掛新增一個標記,方便對應到具體外掛。

image

2,公共DLL

公共DLL類,即復現場景時,主程式和外掛們都需要引用的類。
因為要生成不同版本的公共DLL,所以我們依次修改程式碼,並在生成屬性中設定版本號,然後編譯生成DLL。
依次修改4次,共計4個版本的公共DLL:v1.0、v2.0、v3.0、v4.0,其程式碼修改如下:

v1.0

image

v2.0

image

v3.0

image

v4.0

image

3,外掛

這些外掛除了引用的公共DLL版本不一致外,基本方法都是一樣的:繼承並實現介面。
這裡由於“公共DLL v3.0”中對方法進行了過載,所以我們這裡用兩個外掛來分別呼叫每一方法。

外掛1”程式碼:

image

外掛2”程式碼:

image

外掛3”程式碼:

image

外掛4”程式碼:

image

外掛5”程式碼:

image

4,主程式

主程式的介面設計如下:

image

其中:
“主程式”按鈕作用:直接呼叫公共DLL中的方法。
“外掛1” - “外掛4” 按鈕作用:呼叫外掛中的方法。

因為要復現場景,所以主程式也需要生成多個版本。
又因為在公共DLL v3.0中過載了方法,所以多分一個版本,來分別呼叫這兩個方法。
所以最終會生成5個版本的主程式。
最終生成的檔案結構如下:

image

主程式的程式碼,主要分為3部分:

3.1,載入MEF外掛

在程式啟動時,我們需要載入所有外掛。

image

3.2,呼叫外掛方法

因為所有的外掛都是基於統一的介面,所以我們先寫一個通用的呼叫外掛方法,然後在點選按鈕時,直接傳入外掛對應匯出標記即可。

image

3.3,主程式呼叫公共DLL方法

對公共DLL的方法呼叫與外掛並無二致,5個版本的主程式,其程式碼變化如下:

v1.0

image

v2.0

image

v3.0

image

v4.0

image

v5.0

image

(二)演示

我們依次編譯生成不同版本的主程式,然後依次執行,其結果如下:

v1.0

image

v2.0

image

v3.0

image

v4.0

image

v5.0

image

可以發現,當月主程式與外掛都引用相同的公共DLL後,無論外掛引用的公共DLL版本是多少,呼叫的均是主程式所引用的公共DLL版本。
當外掛與主程式引用的公共DLL中方法發生改變(如增減引數、修改返回值、刪除了方法等),外掛將會呼叫失敗,丟擲異常。


三、解決方案

問題已復現,那麼我們該如何解決呢?
解決目標就是主程式與外掛們,各自呼叫各自所引用版本的“公共DLL”。在本示例中,就是:外掛1引用“公共DLL v1.0”,外掛2引用“公共DLL v2.0”,等等。

而解決方案非常之簡單,簡單到一句話就能說完:
為公共DLL新增強簽名

關於“強簽名”,本文不再贅述,請參考MSDN。
下面,我們來對示例進行修改。

(一)新增強簽名

我們在公共DLL上右鍵,選擇“屬性”,然後選擇“簽名”,按提示新增強簽名即可。

image

(二)重新生成外掛和主程式。

我們依次重新生成外掛,和主程式,過程不再贅述。

(三)演示

我們重新依次執行5個版本的主程式,會發現問題已解決,主程式與外掛們都各自呼叫了自己所引用版本的公共DLL。
其與未進行強簽名時的執行結果對比如下:

v1.0

image

v2.0

image

v3.0

image

v4.0

image

v5.0

image


四、總結

說實話,本篇文章所描述的問題,雖然不難,解決辦法也很簡單,卻曾經困擾了我好久。
之前一直沒有找到現成的可行解決方案,曾在博問上提問過,答案雖然有用,但無奈不知怎麼去應用。這事也就放下了。

最近在看《CLR via C#》,曾經看過,但走馬觀花、不知所云、無甚收穫。此次重看,發現已可以讀懂,頗有感獲。
在看到了強簽名時,驀然發現,這不就是曾經困擾我很久的解決方法嗎?
既心動便行動,經過一番測試,果然可行。
挺感慨的,果然往基礎的方向去學習,是正確的。

本人水平有限,難免有所疏漏,歡迎各位讀者評論指正。


五、原始碼下載

https://files.cnblogs.com/files/lesliexin/MEFDemo.zip


-【END】-

相關文章