延遲加載是一個很大的誘惑,能夠達到一些比較好的效果,好比:html
一、在實體框架中,因爲關聯數據的數量和使用時機是不肯定的,經過延遲加載,僅在使用的時候去執行關聯數據的查詢操做,減小無謂的數據查詢操做,能夠下降單次數據查詢執行的時間,提高系統的性能。web
二、在一個插件平臺中啓動平臺時只加載必需的程序集,當執行到具體插件時再加載插件相關的程序集,僅在須要的時候加載資源,能夠減小插件平臺的啓動時間,使內存的佔用更合理些。多線程
延遲加載可使資源的佔用更加合理,並提高必定的性能,固然也有一些例子來講明延遲加載的壞處,這就須要根據實際的狀況去考量,不是這篇文章的目的。app
言歸正傳,在ASP.NET Web Forms開發模式中,程序集通常都放到bin目錄下,或者在web.config中經過配置codebase或者probing節點指定程序集目錄,應用程序啓動時會從這些位置自動加載程序集。咱們要使用延遲加載,就不能將程序集放到這些地方,將須要延遲加載的程序集放到一些有規則可循的目錄是一種比較好的方式。好比:框架
rootdom
|–bin性能
|–lazyloadui
| |–bin1.net
| |–bin2插件
將這些程序集都放到一個lazyload的目錄中,而後在其中根據程序集的劃分創建不一樣的子目錄,根據須要去不一樣的目錄中加載程序集。
那麼使用什麼方法加載程序集呢?
Assembly類提供了幾個靜態方法:Load、LoadFile、LoadFrom,能夠經過這幾個方法將dll文件加載到當前應用程序域的程序集中。
關於這幾個方法如何選擇,網上有一些總結,這裏不作討論。如下是一些總結:
http://www.cnblogs.com/xuqingfeng/archive/2012/05/22/assembly-load-loadfrom-loadfile-details.html
http://msdn.microsoft.com/zh-cn/library/dd153782(v=vs.110).aspx
實現程序集的延遲加載須要擴展兩個地方:
一、依賴程序集的延遲加載
經過訂閱當前應用程序域的AssemblyResolve事件,應用程序域在加載依賴程序集時若是找不到就會觸發這個事件。
在這個事件中咱們能夠經過一些規則找到須要加載的程序集文件,而後經過Assembly的加載方法加載到內存,並返回。
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; |
private Assembly CurrentDomain_AssemblyResolve( object sender, ResolveEventArgs args) |
{ |
Assembly assembly = null ; |
//加載程序集部分省略 |
return assembly; |
} |
二、頁面動態編譯所需程序集的延遲加載
aspx頁面在首次訪問時會進行編譯,編譯時須要頁面綁定的類所在的程序集。默認狀況下這些程序集是在程序啓動的時候自動加載的,從.net4開始,微軟提供了一個應用程序啓動的擴展支持System.Web.PreApplicationStartMethod,也能夠在這裏經過程序加載程序集,但仍是達不到延遲加載的效果。
aspx頁面的編譯是經過BuildManager實現的,調用BuildManager.AddReferencedAssembly方法能夠添加程序集,可是這個方法只能在上邊提到的擴展支持中調用,程序啓動後再調用就會拋出異常(多是.net4.0還有些東西沒協調好),此路不通。既然不能經過方法添加,那是否是能夠直接加入到BuildManager的程序集集合中,很不幸全是私有的,有興趣的能夠本身反編譯看看。
私有的其實也不是沒有辦法能夠獲取,使用反射,還好BuildManager有一個靜態的屬性TheBuildManager,經過反射獲取這個屬性的值就能夠獲得內部的BuildManager實例,修改程序集的集合就不成問題了。
// 獲取BuildManager的實例 |
PropertyInfo buildmanagerProperty = Type.GetTypeFromHandle( typeof (BuildManager).TypeHandle).GetProperty( "TheBuildManager" , BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.GetProperty); |
BuildManager buildmanager = buildmanagerProperty.GetValue( null , null ) as BuildManager; |
// 獲取TopLevelReferencedAssemblies |
PropertyInfo topLevelReferencedAssembliesProperty = Type.GetTypeFromHandle( typeof (BuildManager).TypeHandle).GetProperty( "TopLevelReferencedAssemblies" , BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetProperty); |
IList assemblies = topLevelReferencedAssembliesProperty.GetValue(buildmanager, null ) as IList; |
// 添加程序集 |
Assembly assembly = null ; |
//加載程序集部分省略 |
assemblies.Add(assembly); |
這段程序要在頁面編譯以前調用,好比PageHandlerFactory的GetHandler方法中。
經過這兩個擴展基本上就能夠實現程序集的延遲加載了,能用來幹什麼就要看本身了。博客園有我的搞了個OSGI.NET,就用到文中的兩個方法。
固然上邊只是初步給出瞭解決問題的方法,若是要實際使用,可能要考慮更多的問題,好比多線程同步問題、程序集多版本問題等等,有興趣的能夠寫寫看。
本人獨立博客地址:http://blog.bossma.cn/dotnet/asp-net-how-to-lazy-load-assembly/
轉載請註明出處。