所以我就去找其它方法,透過 Assembly 來載入,還是一樣,然後就在網路上找到有人提供一個解決方案了
| 1 | byte[] dllBytes = File.ReadAllBytes("MyDll.dll"); |
| 2 | Assembly assembly = Assembly.Load(dllBytes); |
| 3 | MyClass instance = (MyClass)assembly.CreateInstance("MyDll.MyClass"); |
| 開新視窗(view plain) | 列印(print) | ? |
所以就繼續找了下一個方法,既然是因為 AppDomain 的快取機制,那我就再創一個 AppDomain 就行了吧,事實證明這樣的確是成功解決了,因為 AppDomain 才能作卸載的動作(Assembly 是不提供卸載的)
Common:這裡面放著通用函數及介面,其中在裡面有定義一個 IReport 就是報表的 Interface
Report:這裡面放著報表用的類別庫,當然他會參考 Common ,並實作 IReport
WebApp:這裡則是放著 ASP.Net 網頁,線上報表頁面都在裡面,其中他會同時參考 Common 及 Report,因為 WebApps 沒有鎖定 dll 的問題,所以就算不用動態加載對編輯環境而言也不會有差異,所以並不需要特別讓他去動態加載報表的類別
WinService:這裡放著我撰寫好的服務,基本上一開始,連登入桌面也不用,他就自動開始啟動了,每十分鐘就會查詢一次資料庫,如果有需要產出報表才會去呼叫相對應的報表檔產生函數並將產出的報表寄送出去,其中他會參考 Common 類別
其中因為 Service 是十分鐘才輪一次,在這十分鐘的空檔他不應該對任何編譯造成阻礙,畢竟那十分鐘他在睡覺,而實事是不透過新建 AppDomain 的方式,他就是會鎖定原始的 dll 檔,所以才需要提出這樣的動態加卸載機制
| 1 | using System; |
| 2 | using System.Collections.Generic; |
| 3 | using System.ComponentModel; |
| 4 | using System.Data; |
| 5 | using System.Diagnostics; |
| 6 | using System.Linq; |
| 7 | using System.ServiceProcess; |
| 8 | using System.Text; |
| 9 | using System.Reflection; |
| 10 | using System.Configuration; |
| 11 | using System.Security.Policy; |
| 12 | using System.Runtime.Remoting; |
| 13 | using System.IO; |
| 14 | |
| 15 | namespace WinServices |
| 16 | { |
| 17 | public class DynamicLoadReportDLL : IDisposable |
| 18 | { |
| 19 | private AppDomain appDomain; |
| 20 | private AppDomainSetup appDomainSetup; |
| 21 | private string assemblyFile; |
| 22 | private Evidence evidence; |
| 23 | |
| 24 | public void Load(string AssemblyFile) |
| 25 | { |
| 26 | assemblyFile = AssemblyFile; |
| 27 | if (appDomainSetup == null) |
| 28 | { |
| 29 | string pluginDirectory = Path.GetDirectoryName(AssemblyFile); |
| 30 | appDomainSetup = new AppDomainSetup(); |
| 31 | appDomainSetup.ApplicationName = "RoutineReportDynamicDLL"; |
| 32 | appDomainSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory; |
| 33 | appDomainSetup.PrivateBinPath = pluginDirectory; |
| 34 | appDomainSetup.CachePath = Path.Combine(pluginDirectory, "Cache/"); |
| 35 | appDomainSetup.ShadowCopyFiles = "true"; |
| 36 | appDomainSetup.ShadowCopyDirectories = pluginDirectory; |
| 37 | Evidence baseEvidence = AppDomain.CurrentDomain.Evidence; |
| 38 | evidence = new Evidence(baseEvidence); |
| 39 | appDomain = AppDomain.CreateDomain("RoutineReportDynamicDLL_Domain", evidence, appDomainSetup); |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | public Common.IReport GetReport(string typename, DateTime timeline) |
| 44 | { |
| 45 | return (Common.IReport)appDomain.CreateInstanceFromAndUnwrap(assemblyFile, typename, false, BindingFlags.Default, null, new object[] { timeline }, null, null, evidence); |
| 46 | } |
| 47 | |
| 48 | public void UnLoad() |
| 49 | { |
| 50 | if(appDomain != null) |
| 51 | AppDomain.Unload(appDomain); |
| 52 | appDomain = null; |
| 53 | GC.Collect(); |
| 54 | GC.WaitForPendingFinalizers(); |
| 55 | GC.Collect(0); |
| 56 | } |
| 57 | |
| 58 | #region IDisposable 成員 |
| 59 | |
| 60 | public void Dispose() |
| 61 | { |
| 62 | UnLoad(); |
| 63 | } |
| 64 | |
| 65 | #endregion |
| 66 | } |
| 67 | } |
| 開新視窗(view plain) | 列印(print) | ? |
這類別主要是建構後透過 Load 函數建構一個新的 AppDomainSetup 來儲存 AppDomain 的設定
之後則透過 AppDomain.CreateDomain 函數來建構一個新的 AppDomain 給這類別庫使用
而 UnLoad 方法則將 Load 建構出來的 appDomain 卸載掉,並執行 GC 的命令回收記憶體資源
GetReport 則是真正要呼叫的方法,讓他建立一個 Report 類別(類別的名稱是 typename 控制)的實體,而建構子的參數則是 timeline
透過這樣的方式,服務每十分鐘到時,他只要呼叫這類別的 Load 語法,就能建構出要動態參考的 dll 的 appDomain 了
而在 GetReport 時實作 dll 的類別並回傳給服務,服務只要呼叫他後就能執行指定的類別產生相關的報表了
最後只要透過 UnLoad 中釋放 appDomain ,就可以釋放 dll ,讓他不被鎖定也不會造成記憶體的負擔
ref:
http://mywct.pixnet.net/blog/post/38622519-%E5%8B%95%E6%85%8B%E5%8A%A0%E5%8D%B8%E8%BC%89-dll-%E6%AA%94
http://www.cnblogs.com/tianma3798/p/7497272.html
http://dudu.cnblogs.com/archive/2004/03/04/2182.html