在上文解决NopCommerce 在iis缓存目录Temporary ASP.NET Files下存在两个版本的dll问题(一)中已经已经知道了在Nopcommerce中,只要插件项目里引用第三方dll时,把“复制本地”设置为False,插件就不会把第三方的dll复制到iis缓存目录,也就不会出现两个版本的dll问题。但是后来想想,在自己的插件引用的文件而站点没有引用的情况下,把插件引用的dll“复制本地”设置为False,那站点还是会有找不到引用文件的情况。于是就再去看看那个复nopcommerce项目专门处理插件输入目录的类PluginManager。
在NopCommerce 3.9.0版的PluginManager类140行,有如下代码:
//load all other referenced assemblies now foreach (var plugin in pluginFiles .Where(x => !x.Name.Equals(mainPluginFile.Name, StringComparison.InvariantCultureIgnoreCase)) .Where(x => !IsAlreadyLoaded(x))) PerformFileDeploy(plugin);
这段代码就是为了加载插件引用的第三方dll的。上篇文章提到了,官方文档是推荐插件引用的dll全部把“复制本地”设置为False
Important: Going forward make sure "Copy local" properties of all third-party assembly references (including core libraries such as Nop.Services.dll or Nop.Web.Framework.dll) are set to "False" (do not copy)
这里官方文档没有说全,实际上应该是如果站点已经引用到的情况下,插件引用的dll应该把“复制本地”设置为False,站点没有引用的,还是要照常输出到插件输出目录。
再回过头来看类PluginManager加载插件引用dll的代码,既然已经有了这个方法,为何还会出现版本不一致的问题。看着这代码,最大的嫌疑就是IsAlreadyLoaded这个方法了,如果IsAlreadyLoaded为false,那么就会去插件输出目录寻找dll。
看看IsAlreadyLoaded方法:
private static bool IsAlreadyLoaded(FileInfo fileInfo) { //compare full assembly name //var fileAssemblyName = AssemblyName.GetAssemblyName(fileInfo.FullName); //foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) //{ // if (a.FullName.Equals(fileAssemblyName.FullName, StringComparison.InvariantCultureIgnoreCase)) // return true; //} //return false; //do not compare the full assembly name, just filename try { string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileInfo.FullName); if (fileNameWithoutExt == null) throw new Exception(string.Format("Cannot get file extension for {0}", fileInfo.Name)); foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) { string assemblyName = a.FullName.Split(new[] { ',' }).FirstOrDefault(); if (fileNameWithoutExt.Equals(assemblyName, StringComparison.InvariantCultureIgnoreCase)) return true; } } catch (Exception exc) { Debug.WriteLine("Cannot validate whether an assembly is already loaded. " + exc); } return false; }
由此可见,PluginManager类判断dll是否已经加载是用
AppDomain.CurrentDomain.GetAssemblies()
调试发现,清空iis缓存目录第一次debug,进入插件初始化时,AppDomain.CurrentDomain.GetAssemblies()这方法加载89个程序集。
第二次调试时,AppDomain.CurrentDomain.GetAssemblies()这个方法却只加载了22个程序集, 第二次调试时,虽然插件目录里出现的其他dll在iis缓存目录里存在,但这个方法都没获取到,所以会插件目录里的其他dll会被加载到iis缓存目录。
但是为什么两次启动调试AppDomain.CurrentDomain.GetAssemblies()获取到的程序集会不一样呢?跟iis的机制有关?暂时不得而知。
总结:nopcommerce进行插件开发的时候,引用的第三方文件如果是站点已经引用了的,需要把引用“复制本地”设置为False,如果站点没有引用,则还是要复制本地,否则如果不是全量更新而是抽包更新,会很容易出现插件目录dll与站点bin目录版本不同的情况,导致更新失败。