解决NopCommerce 在iis缓存目录Temporary ASP.NET Files下存在两个版本的dll问题(二)

在上文解决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目录版本不同的情况,导致更新失败。

原文地址:https://www.cnblogs.com/basterdaidai/p/8098251.html