[.NET] 將共用的元件庫放在指定的目錄中

其實這已經是個老問題了,只是剛好最近公司遇到,結果在實作過程中發現問題並沒有這麼單純
在這篇文章裡,會說明兩種不同最簡單的方式,將元件放在一個共同目錄中讓多個應用程式參考

當然也包含了"指定的目錄"

方法一:將所有的應用程式放在同一個目錄,並將要共用的元件放在子目錄下

用這個方式的話,就會得到下面這張圖的結果

從這張圖可以看到,方式一的處理方式,會將所有的應用程式及其設定檔存放在同一個目錄下,並將共用的元件放在一個共用元件資料夾中,而各別的應用程式參考自己的元件,則放置於個別的資料夾中

要實作的話,只要在app.config中加入下面幾行就可以

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <!-- 指定參考元件的子目錄 -->
      <probing privatePath="Common_Librarys;DataTransApp_Librarys" />
  </runtime>
</configuration>

在這個config的設定中,透過<probing>的Tag,加入了要參考元件的資料夾路徑,就可以把應用程式載入元件的路徑作修改,若是有多個資料夾,就透過分號(;)的方式去區隔就可以。所以透過這樣的設定方式,就可以分別把應用程式要載入的共用元件以及自己要載入的元件分成不同的資料夾作存放並進行管理

使用<probing>的設定
好處在於設定完就可以直接生效
缺點是必須將所有的應用程式攤平在同一個目錄下,因為<probing>的設定無法跳至上一層或是指定的資料夾目錄,只能設定在應用程式所在路徑下的子目錄中

方法二:將應用程式各別放置於自己的資料夾中,並將共用元件放置於一個獨立的子資料夾

 採用這個方式的話,可以得到這樣的結果

這樣的資料夾結構,可以很清楚的將各別的應用程式與要引用的元件獨立放在資料夾中,唯獨共用的元件放在另一個資料夾中。只是這樣作的話,除了改config的設定外,還需要作很多事才能達到,依照下面的步驟就可以完成了

1.點開共用元件的AssemblyInfo.cs檔,並把該填的都填一填

2.在共用元件的專案上按右鍵選擇[屬性]

3.切換到[簽署]的頁籤中,並勾選[簽署組件]=>於下拉式選單中點選<新增...>

4.在[建立強式名稱金鑰]的畫面中,輸入一個金鑰檔名稱,而[以密碼保護我的金鑰檔]則可以視需要看是否要增加,在這裡先不勾選

5.設定完成後,就可以在共用元件的專案中,看到這個金鑰檔已經被產生出來,並且已經勾選了簽署組件的動作

6.接著先將這個元件進行編譯,再打開[命令提示字元](cmd),切換到目錄"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.2 Tools>",並在這個目錄下執行下面的指令

sn -T "共用元件所在的完整路徑及檔名"

然後我們會得到下圖畫面的結果,在這裡最重要的步驟,就是將[Public key token]的值複製下來,等一下在app.config中會用到當然也可以先將這個元件複製到某個目錄下再執行,不一定要直接去處理debug目錄下的檔案

sn.exe這個工具會因為安裝的.NET版本不同而放在不同的目錄底下,可以視狀況進行目錄的切換使用

7.回到Visual Studio,點開應用程式的專案,並將共用元件參考至應用程式專案中,並將[複製到本機]的項目設定為[False]

8.點開應用程式的app.config,將下面的設定值加入至應用程式的app.config之中

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
    </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <!-- 指定元件的讀取目錄 -->
    <dependentAssembly>
        <assemblyIdentity name="CommonLibrary" culture="neutral" publicKeyToken="32ccc5ad468e4da6" />
        <codeBase version="1.0.0.1" href="file://e:/CommonLibrarys/CommonLibrary.dll"/>
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

在這個設定中,主要是將參考元件的publicKeyToken以及相關的資訊放入至應用程式之中

1.publicKeyToken的屬性一定要放入至app.config後,並重新編譯一次應用程式,若是未重新編譯應用程式就直接更改現有的config是無效的
2.[publicKeyToken]、[version]、[href]、[name]這四個屬性名稱,大小寫請注意,如果大小寫不同的話,會影響到參考結果
3.[version]與[href]的設定可以待放至執行環境後再修改,這兩個設定可以直接修改app.config並套用,但是[publicKeyToken]與[name]則是一定要將應用程式重新編譯一次才能套用

 9.這時候重新去編譯並執行應用程式應該是會出錯的,因為會發生找不到元件的錯誤,主要的原因是因為在href設定的路徑中是找不到這個共用元件的如果指定的路徑有這個共用檔案就可以順利被執行,可以將共用元件與設定檔中的路徑對應起來後執行看結果,如果正確可以執行,代表加上金鑰的共用元件與應用程式的對應是沒有問題的

10.將編譯好的共用元件檔,放到指定的路徑中

11.將編譯好的應用程式放到指定的目錄中

12.打開應用程式的app.config,並修改要載入共用元件的路徑,請記得version、publicKeyToken的值必須與共用元件一致,大小寫也必須與文中的設定一樣

13.點選應用程式並執行,可以看到應用程式有被正確執行並載入元件運作了

使用codeBase指定元件路徑的方式
好處在於可以將共用元件放至於任何指定的路徑下,不用受限於應用程式的子目錄
缺點是必須將這個共用元件加上強式簽署的金鑰,並且必須透過sn.exe的工具找出publicKeyToken並登錄在app.config之中

 最後,再提醒一下

app.config裡的設定,大小寫一定要正確,不然會完全找不到問題點

參考資料:
[.NET]將共用的DLL放在同一個目錄之中
[.NET] 有關強式名稱 (Strong Name) 的兩三事
如何在執行階段載入組件,該執行階段不位於應用程式 Bin 資料夾中
<probing> Element
<codeBase> Element
<runtime> 的 <assemblyIdentity> 項目
Specifying an Assembly's Location
建立和使用強式名稱的組件
How to: Create a Tool to Get the Public Key of an Assembly
Getting the PublicKeyToken of .Net assemblies