在编译aspx页面的动态程序集期间,不会触发AssemblyResolve事件

这个真的在惹恼我。 开始:

我的目标是在运行时加载包含嵌入式aspx,ascx等的程序集。我还想要的是不要将程序集文件锁定在磁盘上,以便我可以在运行时更新它而无需重新启动应用程序(我知道)这将使以前的版本加载)。

为此,我编写了一个虚拟路径提供程序来完成这一操作。 我订阅了CurrentDomain.AssemblyResolve事件,以便将框架重定向到我的程序集。

问题是,当框架尝试编译aspx页面的动态程序集时,我得到以下内容:

编译器错误消息:CS0400:在全局命名空间中找不到类型或命名空间名称“Pages”(您是否缺少程序集引用?)

源错误:公共类app_resource_pages__version_1_0_0_0__culture_neutral__publickeytoken_null_default_aspx:global :: Pages._Default,System.Web.SessionState.IRequiresSessionState,System.Web.IHttpHandle

我注意到,如果我使用Assembly.Load(AssemblyName)或Assembly.LoadFrom(filename)加载程序集,我不会得到上述错误。 如果我用Assembly.Load(byte [])加载它(以便不锁定它),抛出exception但是我的AssemblyResolve处理程序在被调用时正确地返回程序集(它被调用一次)。

所以我猜测它在框架解析asp标记时调用一次,而不是在它尝试为aspx页面创建动态程序集时调用。

我不确定是什么导致了缺少的程序集引用,但是如果我们退一步并且到达程序按预期工作的点,那么我们必须解决另一个问题。 此问题是加载的程序集的锁定。 .Net框架始终锁定已加载的程序集。 您可以更新bin文件夹中的dll文件的原因实际上是一个技巧。 你看, AppDomain有一个很好的属性叫做ShadowCopyDirectories ,它指示在加载程序集时将被复制阴影的目录。 因此,通过更改阴影复制目录列表,您可以从任何文件夹加载而无需锁定程序集:

  protected const string ApplicationAssembliesFolder = "~/Assemblies"; protected void Application_Start(object sender, EventArgs e) { var assembliesPath = Server.MapPath(ApplicationAssembliesFolder); AppDomain.CurrentDomain.SetShadowCopyPath( AppDomain.CurrentDomain.SetupInformation.ShadowCopyDirectories + Path.PathSeparator + assembliesPath); Assembly.LoadFrom( Path.Combine(assembliesPath, "Example.dll")); } 

我想我得到了以下工作:

  public Assembly GetAssembly() { Assembly result = cache.Get(assemblyKey) as Assembly; if (result == null) { lock (this) { result = cache.Get(assemblyKey) as Assembly; if (result == null) { assemblyName = System.Reflection.AssemblyName.GetAssemblyName(assemblyFile); result = Assembly.Load(assemblyName); cache.Add(assemblyKey, result, new CacheDependency(assemblyFile), Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.High, new CacheItemRemovedCallback(OnAssemblyRemoved)); } } } return result; } 

这仅适用于新程序集与旧程序集的版本不同的情况。 应用程序不会重新启动,但会加载新程序集。 我尝试更新aspx页面的标记和代码隐藏,它按预期工作。

总结一下:

  1. AppDomain.CurrentDomain.SetShadowCopyPath
  2. Assembly.Load(AssemblyName.GetAssemblyName(assemblyFile))