2012-07-05 33 views
0

我正在開發一個解決方案,其中服務在後臺連續運行,並且可以在運行時添加/刪除插件DLL。該服務將在需要時加載必要的插件,運行並卸載它們。這是卸載部分是目前給我帶來的麻煩:一旦某個類第一次成功加載(變量tc),即使更新了DLL文件,它也不會重新加載。我想我不會正確地卸載班級/程序集/ appdomain,所以我希望得到一些關於走這最後一英里的建議。C#如何正確地從內存中卸載該類?

編輯:我更新了這篇文章,以反映代碼中的最新變化,並解釋卸載的確切時間沒有影響:Linux Ubuntu(通過Mono)沒有出現問題,但它存在於Windows 2008 Server上,我試圖用一個更新的文件版本替換某個插件DLL。看起來,.NET框架已經緩存在程序集的某個地方,並且很快樂,不用重新加載它。 DLL文件名不會改變,但File Version屬性不同,所以我期望運行時將以前加載的DLL版本與加載的版本進行比較,如果版本號不同,則使用新版本。如果我稍微更改代碼以從具有不同名稱的DLL文件加載程序集,則重新加載按預期發生。

using System; 
using System.Reflection; 

namespace TestMonoConsole 
{ 
    public interface ITestClass 
    { 
     void Talk(); 
    } 
    class MainClass 
    { 
     public static void Main (string[] args) 
     { 
      string pluginPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); 

      string classAssembly = "TestClass"; 
      string className = "TestMonoConsole.TestClass"; 
      string command = ""; 
      do 
      { 
       try 
       { 
        System.AppDomain domain = System.AppDomain.CreateDomain(classAssembly); 
        string pluginAssemblyFile = pluginPath + "/" + classAssembly + ".dll"; 
        System.IO.StreamReader reader = new System.IO.StreamReader(pluginAssemblyFile, System.Text.Encoding.GetEncoding(1252), false); 
        byte[] b = new byte[reader.BaseStream.Length]; 
        reader.BaseStream.Read(b, 0, System.Convert.ToInt32(reader.BaseStream.Length)); 
        domain.Load(b); 
        reader.Close(); 
        ITestClass tc = (ITestClass) Activator.CreateInstance(domain, classAssembly, className).Unwrap(); 
        tc.Talk(); 
        System.AppDomain.Unload(domain); 
       } 
       catch (System.IO.FileNotFoundException e) 
       { 
        Console.WriteLine (String.Format("Error loading plugin: assembly {0} not found", classAssembly)); 
       } 
       command = Console.ReadLine(); 
      } while (command == ""); 
     } 
    } 
} 
+0

任何異常都會被捕獲或登錄到事件查看器中。如果是,那麼請在這裏發佈例外 – HatSoft 2012-07-05 19:18:27

+0

我在某處讀到一個常見的技巧是生成一個新的應用程序域,並在不再需要它時終止它 – rekire 2012-07-05 19:20:52

+0

在事件查看器中沒有捕獲任何異常,也沒有任何事件發生。 – Passiday 2012-07-05 22:00:50

回答

2

您正在直接在主機域中創建類型。您需要指定域時使用Activator.CreateInstance每http://msdn.microsoft.com/en-us/library/ms224132(v=vs.90).aspx

+0

謝謝,使用Activator.CreateInstance(AppDomain,String,String)實際上解決了這個問題,但是,令人驚訝的是,僅在我的Ubuntu上(通過Mono),而不是在Windows 2003 Server上(實際的部署操作系統)。 – Passiday 2012-07-05 21:02:21

0

此外,而不是重新創建此機制,您可以利用託管加載項框架(MAF,System.AddIn)爲您執行此操作。查看here快速入門。

0

無法從AppDomain中卸載類型和程序集。 因此,您需要創建新的AppDomain,加載類型,完成所有工作,然後卸載該域。

0

您正在實例化您自己的AppDomain中的插件類型。在多次實現插件框架之後,我強烈建議考慮使用MEF(託管擴展性框架)作爲解決方案,因爲它處理這些代碼隔離的常見問題。如果你不想要MEF,一種方法是通過實施一個「遠程控制」類來充當應用程序域之間的通信器。您可以調用遠程類中的一個方法來實例化並運行輔助應用程序域中的代碼。

MEF Documentation

+1

我不相信MEF提供應用程序域隔離。你正在考慮的框架是MAF--我在下面提到。但是,MEF可以與MAF一起使用,以提供非常可擴展的應用程序插件框架。 – Ani 2012-07-05 20:19:29

+0

謝謝,我會看看MEF是否適合我的需求。然而,當我手上有一個「近乎成功」的代碼時,我真的不想重新發明輪子,但總是很高興看到它被修復。 – Passiday 2012-07-05 21:06:10

+0

@ananthonline你是對的,我的錯。 – doogle 2012-07-05 22:22:24

0

this MSDN article中,CLR不使用默認的影子複製。您可以嘗試按以下步驟啓用它:

  1. 創建一個AppDomainSetup
  2. 在AppDomainSetup實例中,將ShadowCopyFiles設置爲true。
  3. 在AppDomainSetup實例中,將ShadowCopyFilesDirectories設置爲包含您希望在運行時可以覆蓋的程序集的目錄路徑。這可能只是插件程序集所在的目錄。
  4. 在AppDomainSetup實例中根據您的需要設置其他所有內容。
  5. 使用以AppDomainSetup作爲參數的AppDomain.CreateAppDomain的重載之一。
  6. 嘗試更換插件組件。

它在Mono上而不是在CLR(Windows)上工作的事實可能意味着CLI不指定是否應默認啓用陰影複製。