延迟加载是一个很大的诱惑,能够达到一些比较好的效果,好比: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/
转载请注明出处。