The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

ASP.NET 如何動態載入組件(Assembly)

如果你的 IIS 中有許多相關站台、網站、或 Web,但每個不同的網站都會使用到相同組件的話,通常的作法是將該組件註冊進 GAC ( Global Assembly Cache )。我最近就在想有沒有辦法就將組件放在特定目錄下,然後所有的 ASP.NET 就動態載入這個目錄中的特定幾個共用的組件,以下是研究的心得分享。

我將整個過程分成「開發階段」與「部署階段」,並假設以下情境:

  • 假設共用的組件檔名為 "ShareCL.dll"。
  • 部署階段時要將 ShareCL.dll 放一份到特定目錄下,假設共用的目錄為 "D:\ShareDLL\"。

開發階段

在開發階段的時候,那些共用的組件一樣放到各專案的 bin 目錄下,所以在 Visual Studio 中開發的時候,一樣可以享用 Intellisense 的好處,整個開發過程就跟平常開發時一樣。

但由於我們將網站部署到正式主機時,組件並不在各網站的 bin 目錄下,而是在共用的目錄裡,這時就會發生組件解析失敗的錯誤:

無法載入檔案或組件 'ShareCL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' 或其相依性的其中之一。 系統找不到指定的檔案。

我們只要修改 Global.asax 的 Application_Start() 事件中的程式碼,告訴 AppDomain 在解析/找尋組件失敗的時候,要特別去我指定的��方載入共用的組件,我們要靠的是 AppDomain.AssemblyResolve 事件:

protected void Application_Start() {
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(MyAssemblyResolver);
}

static Assembly MyAssemblyResolver(object sender, ResolveEventArgs args) {
    AppDomain domain = (AppDomain)sender;
    byte[] rawAssembly = System.IO.File.ReadAllBytes(@"D:\ShareDLL\ShareCL.dll");
    byte[] rawSymbolStore = System.IO.File.ReadAllBytes(@"D:\ShareDLL\ShareCL.pdb");
    Assembly assembly = domain.Load(rawAssembly, rawSymbolStore);
    return assembly;
}

加上這段程式碼之後,當網站應用程式找不到我們的共享組件時,就會自動到 D:\ShareDLL\ShareCL.dll 動態載入組件了,而且也只有「找不到」組件時才會執行這個事件方法,因此並不會影響開發作業。

部署階段

部署網站時就可以不用再針對每個網站更新 bin 目錄下的組件了,只要將組件更新到共享的組件目錄中即可。

不過由於組件是在 Application-Level 載入的,所以就算你更新了網站中的組件,事實上也不會立即生效,而是當應用程式重新啟動時才會重新載入更新過的組件,這部分可以透過重新啟動 IIS 中的應用程式集區(Application Pool)即可達到此需求。

如果你想列出目前 AppDomain 所載入的組件有哪些,可以用以下程式碼取得:

Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

foreach (Assembly assembly in assemblies)
{
    Response.Write(assembly.FullName + "<br/>");
}

相關連結