zoukankan      html  css  js  c++  java
  • Orchard详解--第八篇 拓展模块及引用的预处理

      从上一篇可以看出Orchard在处理拓展模块时主要有两个组件,一个是Folder另一个是Loader,前者用于搜索后者用于加载。
      其中Folder一共有三个:Module Folder、Core Folder、ThemeFolder。Loader有引用加载器(Referenced Module Loader)、核心模块加载器(Core Module Loader)、预编译模块加载器(Precompiled Module Loader)、动态模块加载器(Dynamic Module Loader)。它们在代码里可以看到,在创建容器时对与Folder和Loader的注册:

     1               builder.RegisterType<ExtensionLoaderCoordinator>().As<IExtensionLoaderCoordinator>().SingleInstance();
     2                         builder.RegisterType<ExtensionMonitoringCoordinator>().As<IExtensionMonitoringCoordinator>().SingleInstance();
     3                         builder.RegisterType<ExtensionManager>().As<IExtensionManager>().SingleInstance();
     4                         {
     5                             builder.RegisterType<ExtensionHarvester>().As<IExtensionHarvester>().SingleInstance();
     6                             builder.RegisterType<ModuleFolders>().As<IExtensionFolders>().SingleInstance()
     7                                 .WithParameter(new NamedParameter("paths", extensionLocations.ModuleLocations));
     8                             builder.RegisterType<CoreModuleFolders>().As<IExtensionFolders>().SingleInstance()
     9                                 .WithParameter(new NamedParameter("paths", extensionLocations.CoreLocations));
    10                             builder.RegisterType<ThemeFolders>().As<IExtensionFolders>().SingleInstance()
    11                                 .WithParameter(new NamedParameter("paths", extensionLocations.ThemeLocations));
    12 
    13                             builder.RegisterType<CoreExtensionLoader>().As<IExtensionLoader>().SingleInstance();
    14                             builder.RegisterType<ReferencedExtensionLoader>().As<IExtensionLoader>().SingleInstance();
    15                             builder.RegisterType<PrecompiledExtensionLoader>().As<IExtensionLoader>().SingleInstance();
    16                             builder.RegisterType<DynamicExtensionLoader>().As<IExtensionLoader>().SingleInstance();
    17                             builder.RegisterType<RawThemeExtensionLoader>().As<IExtensionLoader>().SingleInstance();
    18                         }

      这里需要注意的是,除了以上的Loader外,这里还有一个Raw Theme Extension Loader。
      接下来看Folder是如何工作的:
      三个Folder的实现可以说是一致的,都是通过ExtensionHarvester去获取一个ExtensionDescriptior列表:

      

    1      public IEnumerable<ExtensionDescriptor> AvailableExtensions() {
    2             return _extensionHarvester.HarvestExtensions(_paths, DefaultExtensionTypes.Module, "Module.txt", false/*isManifestOptional*/);
    3         }

      仅仅是路径和文件名称(Module.txt和Theme.txt)不同。所以可以这样说,模块和主题的搜索工作是通过ExtensionHarvester完成的。

     1     public IEnumerable<ExtensionDescriptor> HarvestExtensions(IEnumerable<string> paths, string extensionType, string manifestName, bool manifestIsOptional) {
     2             return paths
     3                 .SelectMany(path => HarvestExtensions(path, extensionType, manifestName, manifestIsOptional))
     4                 .ToList();
     5         }
     6         private IEnumerable<ExtensionDescriptor> HarvestExtensions(string path, string extensionType, string manifestName, bool manifestIsOptional) {
     7             string key = string.Format("{0}-{1}-{2}", path, manifestName, extensionType);
     8            return _cacheManager.Get(key, true, ctx => {
     9                 if (!DisableMonitoring) {
    10                     Logger.Debug("Monitoring virtual path "{0}"", path);
    11                     ctx.Monitor(_webSiteFolder.WhenPathChanges(path));
    12                 }
    13                 return AvailableExtensionsInFolder(path, extensionType, manifestName, manifestIsOptional).ToReadOnlyCollection();
    14             });
    15         }

      从上面代码可以看出Harvester内部还使用了缓存机制,以路径、文件名称和类型(模块还是主题)来作为关键字。它缓存的内容是当前目录下的一个拓展描述列表。
      描述信息包括:

     1     public string Location { get; set; }
     2         public string Id { get; set; }
     3         public string VirtualPath { get { return Location + "/" + Id; } }
     4         public string ExtensionType { get; set; }
     5         public string Name { get; set; }
     6         public string Path { get; set; }
     7         public string Description { get; set; }
     8         public string Version { get; set; }
     9         public string OrchardVersion { get; set; }
    10         public string Author { get; set; }
    11         public string WebSite { get; set; }
    12         public string Tags { get; set; }
    13         public string AntiForgery { get; set; }
    14         public string Zones { get; set; }
    15         public string BaseTheme { get; set; }
    16         public string SessionState { get; set; }
    17         public LifecycleStatus LifecycleStatus { get; set; }
    18 
    19         public IEnumerable<FeatureDescriptor> Features { get; set; }    
    View Code

      功能描述信息包括:

    1         public ExtensionDescriptor Extension { get; set; }
    2         public string Id { get; set; }
    3         public string Name { get; set; }
    4         public string Description { get; set; }
    5         public string Category { get; set; }
    6         public int Priority { get; set; }
    7         public IEnumerable<string> Dependencies { get; set; }    
    View Code

      Harvester中的GetDescriptorForExtension和GetFeaturesForExtension两个方法分别用于通过Module.txt或Theme.txt文件来获取拓展描述和功能描述信息(此处暂未对功能描述信息的依赖列表赋值)。其中拓展会作为一个默认的功能。

      Loader
      当Folder完成描述信息的构建后,Loader将通过这些描述信息来探测和加载模块。以下是Loader的接口定义:

     1     public interface IExtensionLoader {
     2         int Order { get; }
     3         string Name { get; }
     4 
     5         IEnumerable<ExtensionReferenceProbeEntry> ProbeReferences(ExtensionDescriptor extensionDescriptor);
     6         Assembly LoadReference(DependencyReferenceDescriptor reference);
     7         void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceProbeEntry referenceEntry);
     8         void ReferenceDeactivated(ExtensionLoadingContext context, ExtensionReferenceProbeEntry referenceEntry);
     9         bool IsCompatibleWithModuleReferences(ExtensionDescriptor extension, IEnumerable<ExtensionProbeEntry> references);
    10 
    11         ExtensionProbeEntry Probe(ExtensionDescriptor descriptor);
    12         ExtensionEntry Load(ExtensionDescriptor descriptor);
    13 
    14         void ExtensionActivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension);
    15         void ExtensionDeactivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension);
    16         void ExtensionRemoved(ExtensionLoadingContext ctx, DependencyDescriptor dependency);
    17 
    18         void Monitor(ExtensionDescriptor extension, Action<IVolatileToken> monitor);
    19 
    20         /// <summary>
    21         /// Return a list of references required to compile a component (e.g. a Razor or WebForm view)
    22         /// depending on the given module. 
    23         /// Each reference can either be an assembly name or a file to pass to the 
    24         /// IBuildManager.GetCompiledAssembly() method (e.g. a module .csproj project file).
    25         /// </summary>
    26         IEnumerable<ExtensionCompilationReference> GetCompilationReferences(DependencyDescriptor dependency);
    27         /// <summary>
    28         /// Return the list of dependencies (as virtual path) of the given module.
    29         /// If any of the dependency returned in the list is updated, a component depending 
    30         /// on the assembly produced for the module must be re-compiled.
    31         /// For example, Razor or WebForms views needs to be recompiled when a dependency of a module changes.
    32         /// </summary>
    33         IEnumerable<string> GetVirtualPathDependencies(DependencyDescriptor dependency);
    34     }

      从上面接口可以看到Loader有很多方法,它们具有什么功能?
      根据接口,可以将其方法分为以下几类:
      ● 拓展模块:
        ○ 探测:ExtensionProbeEntry Probe(ExtensionDescriptor descriptor);
        ○ 加载:ExtensionEntry Load(ExtensionDescriptor descriptor);
      ● 模块引用:
        ○ 探测:IEnumerable<ExtensionReferenceProbeEntry> ProbeReferences(ExtensionDescriptor extensionDescriptor);
        ○ 加载:Assembly LoadReference(DependencyReferenceDescriptor reference);
      ● 激活与停用:
        ○ 模块激活:void ExtensionActivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension);
        ○ 模块停用:void ExtensionDeactivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension);
        ○ 模块删除:void ExtensionRemoved(ExtensionLoadingContext ctx, DependencyDescriptor dependency);
        ○ 引用激活:void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceProbeEntry referenceEntry);
        ○ 引用停用:void ReferenceDeactivated(ExtensionLoadingContext context, ExtensionReferenceProbeEntry referenceEntry);
      ● 监控:void Monitor(ExtensionDescriptor extension, Action<IVolatileToken> monitor);
      ● 编译需要的引用:IEnumerable<ExtensionCompilationReference> GetCompilationReferences(DependencyDescriptor dependency);
      ● 依赖路径支持:IEnumerable<string> GetVirtualPathDependencies(DependencyDescriptor dependency);  

      按照上面的分类,可以大致猜测Loader的使用过程:探测(模块和引用)---->加载(模块和引用)----->激活引用----->激活模块。然后其它接口用于管理拓展的启用和停用,以及编译时的一些辅助。

      从前一篇文章告诉我们的是Orchard的模块激活是以"Module.txt"文件作为输入然后输出一个System.Type的列表。之前通过Folder已经搜索了所有相关目录,解析每一个目录下的Module.txt或Theme.txt文件,生成了一个Extension Descriptor对象列表。
      现在Orchard将通过这些对象使用Loader来探测实际模块以及实际引用的相关信息:
      先放上代码(位于ExtensionLoaderCoordinator类型的CreateLoadingContext方法):

     1        Logger.Information("Probing extensions");
     2             var availableExtensionsProbes1 = _parallelCacheContext
     3                 .RunInParallel(availableExtensions, extension => 
     4                     _loaders.Select(loader => loader.Probe(extension)).Where(entry => entry != null).ToArray())
     5                 .SelectMany(entries => entries)
     6                 .GroupBy(entry => entry.Descriptor.Id);
     7 
     8             var availableExtensionsProbes = _parallelCacheContext
     9                 .RunInParallel(availableExtensionsProbes1, g =>
    10                     new { Id = g.Key, Entries = SortExtensionProbeEntries(g, virtualPathModficationDates)})
    11                 .ToDictionary(g => g.Id, g => g.Entries, StringComparer.OrdinalIgnoreCase);
    12             Logger.Information("Done probing extensions");
    13 
    14             var deletedDependencies = previousDependencies
    15                 .Where(e => !availableExtensions.Any(e2 => StringComparer.OrdinalIgnoreCase.Equals(e2.Id, e.Name)))
    16                 .ToList();
    17 
    18             // Collect references for all modules
    19             Logger.Information("Probing extension references");
    20             var references = _parallelCacheContext
    21                 .RunInParallel(availableExtensions, extension => _loaders.SelectMany(loader => loader.ProbeReferences(extension)).ToList())
    22                 .SelectMany(entries => entries)
    23                 .ToList();
    24             Logger.Information("Done probing extension references");

      从上面代码可以看到它做了三件事:1、探测所有的拓展信息。2、根据路径修改时间对探测信息进行排序。3、探测所有引用信息。

    探测拓展信息:

    1 .RunInParallel(availableExtensions, extension => 
    2                     _loaders.Select(loader => loader.Probe(extension)).Where(entry => entry != null).ToArray())

      根据代码可以看出,对于每一个可用拓展,需要通过所有loader的探测:

      1. CoreExtensionLoader:很直接的判断是否来至于Core目录下,然后直接组建ExtensionProbeEntry。

     1     public override ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
     2             if (Disabled)
     3                 return null;
     4 
     5             if (descriptor.Location == "~/Core") {
     6                 return new ExtensionProbeEntry {
     7                     Descriptor = descriptor,
     8                     Loader = this,
     9                     Priority = 100, // Higher priority because assemblies in ~/bin always take precedence
    10                     VirtualPath = "~/Core/" + descriptor.Id,
    11                     VirtualPathDependencies = Enumerable.Empty<string>(),
    12                 };
    13             }
    14             return null;
    15         }

        2. PrecompiledExtensionLoader:根据Id(模块名称)获取程序集文件路径,并组建ExtensionProbeEntry。它的依赖列表也是当前程序集。

     1 ...
     2        Logger.Information("Probing for module '{0}'", descriptor.Id);
     3 
     4             var assemblyPath = GetAssemblyPath(descriptor);
     5             if (assemblyPath == null)
     6                 return null;
     7 
     8             var result = new ExtensionProbeEntry {
     9                 Descriptor = descriptor,
    10                 Loader = this,
    11                 VirtualPath = assemblyPath,
    12                 VirtualPathDependencies = new[] { assemblyPath },
    13             };
    14 
    15             Logger.Information("Done probing for module '{0}'", descriptor.Id);
    16 ...

        3. DynamicExtensionLoader:与PrecompiledExtensionLoader相似,只不过这里获取的是模块的csproj文件。但是这里要注意的是它的依赖信息通过GetDependencies方法获取了相关的文件信息。

     1         Logger.Information("Probing for module '{0}'", descriptor.Id);
     2 
     3             string projectPath = GetProjectPath(descriptor);
     4             if (projectPath == null)
     5                 return null;
     6 
     7             var result = new ExtensionProbeEntry {
     8                 Descriptor = descriptor,
     9                 Loader = this,
    10                 VirtualPath = projectPath,
    11                 VirtualPathDependencies = GetDependencies(projectPath).ToList(),
    12             };
    13 
    14             Logger.Information("Done probing for module '{0}'", descriptor.Id);    
    15 ---
    16  public ProjectFileDescriptor Parse(TextReader reader) {
    17             var document = XDocument.Load(XmlReader.Create(reader));
    18             return new ProjectFileDescriptor {
    19                 AssemblyName = GetAssemblyName(document),
    20                 SourceFilenames = GetSourceFilenames(document).ToArray(),
    21                 References = GetReferences(document).ToArray()
    22             };
    23         }
    24 private static string GetAssemblyName(XDocument document) {
    25             return document
    26                 .Elements(ns("Project"))
    27                 .Elements(ns("PropertyGroup"))
    28                 .Elements(ns("AssemblyName"))
    29                 .Single()
    30                 .Value;
    31         }
    32 
    33         private static IEnumerable<string> GetSourceFilenames(XDocument document) {
    34             return document
    35                 .Elements(ns("Project"))
    36                 .Elements(ns("ItemGroup"))
    37                 .Elements(ns("Compile"))
    38                 .Attributes("Include")
    39                 .Select(c => c.Value);
    40         }
    41 
    42         private static IEnumerable<ReferenceDescriptor> GetReferences(XDocument document) {
    43             var assemblyReferences = document
    44                 .Elements(ns("Project"))
    45                 .Elements(ns("ItemGroup"))
    46                 .Elements(ns("Reference"))
    47                 .Where(c => c.Attribute("Include") != null)
    48                 .Select(c => {
    49                             string path = null;
    50                             XElement attribute = c.Elements(ns("HintPath")).FirstOrDefault();
    51                             if (attribute != null) {
    52                                 path = attribute.Value;
    53                             }
    54 
    55                             return new ReferenceDescriptor {
    56                                 SimpleName = ExtractAssemblyName(c.Attribute("Include").Value),
    57                                 FullName = c.Attribute("Include").Value,
    58                                 Path = path,
    59                                 ReferenceType = ReferenceType.Library
    60                             };
    61                         });
    62 
    63             var projectReferences = document
    64                 .Elements(ns("Project"))
    65                 .Elements(ns("ItemGroup"))
    66                 .Elements(ns("ProjectReference"))
    67                 .Attributes("Include")
    68                 .Select(c => new ReferenceDescriptor {
    69                     SimpleName = Path.GetFileNameWithoutExtension(c.Value),
    70                     FullName = Path.GetFileNameWithoutExtension(c.Value),
    71                     Path = c.Value,
    72                     ReferenceType = ReferenceType.Project
    73                 });
    74 
    75             return assemblyReferences.Union(projectReferences);
    76         }
    View Code

      获取依赖信息,实际上就是解析csproj这个xml文件。

        4. RawThemeExtensionLoader:这个Loader比较特殊,它用于加载~/themes目录下没有代码的主题。如果存在csproj和bin目录下的程序集都会被忽略。

     1        // Temporary - theme without own project should be under ~/themes
     2             if (descriptor.Location.StartsWith("~/Themes",StringComparison.InvariantCultureIgnoreCase)) {
     3                 string projectPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Id,
     4                                            descriptor.Id + ".csproj");
     5 
     6                 // ignore themes including a .csproj in this loader
     7                 if ( _virtualPathProvider.FileExists(projectPath) ) {
     8                     return null;
     9                 }
    10 
    11                 var assemblyPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Id, "bin",
    12                                                 descriptor.Id + ".dll");
    13 
    14                 // ignore themes with /bin in this loader
    15                 if ( _virtualPathProvider.FileExists(assemblyPath) )
    16                     return null;
    17 
    18                 return new ExtensionProbeEntry {
    19                     Descriptor = descriptor,
    20                     Loader = this,
    21                     VirtualPath = descriptor.VirtualPath,
    22                     VirtualPathDependencies = Enumerable.Empty<string>(),
    23                 };
    24             }

        5. ReferencedExtensionLoader:引用拓展比较特殊,因为引用拓展是直接被根项目引用的模块项目,这些模块当根项目编译时,它们也会被编译并放置到根项目的bin目录下。

     1           var assembly = _buildManager.GetReferencedAssembly(descriptor.Id);
     2             if (assembly == null)
     3                 return null;
     4 
     5             var assemblyPath = _virtualPathProvider.Combine("~/bin", descriptor.Id + ".dll");
     6 
     7             return new ExtensionProbeEntry {
     8                 Descriptor = descriptor,
     9                 Loader = this,
    10                 Priority = 100, // Higher priority because assemblies in ~/bin always take precedence
    11                 VirtualPath = assemblyPath,
    12                 VirtualPathDependencies = new[] { assemblyPath },
    13             };

    注:这里BulidManager中是通过一个DefaultAssemblyLoader来加载程序集的,DefaultAssemblyLoader类型自身维护了一个ConcurrentDictionary<string, Assembly> _loadedAssemblies字典,用于通过程序集的Short Name来存取程序集,如果程序集不存在时就通过程序集的Full Name、Short Name甚至通过程序集解析器来解析出程序集的名称,最后通过这些名称依次通过.net的Assembly调用Load(name)的方法加载返回并对结果进行缓存。
    Orchard中有三种名称解析器:AppDomainAssemblyNameResolver、OrchardFrameworkAssemblyNameResolver以及GacAssemblyNameResolver。 它们分别从AppDomain、OrchardFramework依赖的DefaultAssemblyLoader中以及.Net全局程序集中查找程序集。

      从上面的分析可知,它主要的操作是通过ExtensionDescriptor来获取对应模块的程序集路径和优先级,并以当前的加载器打包,以便后续用于加载对应程序集。最终ExtensionProbeEntry是以描述Id进行分组的,意味这同一个拓展存在多个探测实体(来自于不同Loader,主要是有不同的优先级以及路径修改时间)。

    探测信息排序:

      这里主要是将上面获取的ExtensionProbeEntry,针对每一个Key(即ExtensionDescriptor.Id)通过优先级来分组,然后取出优先级最高的组,如果改组中存在多个项(如预编译加载器和动态加载器都探测到相应模块,且它们优先级一致)时,根据它们依赖路径的最新修改时间再次进行排序(上面创建ExtensionProbeEntry时VirtualPathDependencies一般为程序集路径,但是动态加载器是所有依赖文件路径,句话说只要任意文件的修改日期大于编译库的信息,那么动态加载器对应的探测实体就会排在前面优先使用)。

     1         private IEnumerable<ExtensionProbeEntry> SortExtensionProbeEntries(IEnumerable<ExtensionProbeEntry> entries, ConcurrentDictionary<string, DateTime> virtualPathModficationDates) {
     2             // All "entries" are for the same extension ID, so we just need to filter/sort them by priority+ modification dates.
     3             var groupByPriority = entries
     4                 .GroupBy(entry => entry.Priority)
     5                 .OrderByDescending(g => g.Key);
     6 
     7             // Select highest priority group with at least one item
     8             var firstNonEmptyGroup = groupByPriority.FirstOrDefault(g => g.Any()) ?? Enumerable.Empty<ExtensionProbeEntry>();
     9 
    10             // No need for further sorting if only 1 item found
    11             if (firstNonEmptyGroup.Count() <= 1)
    12                 return firstNonEmptyGroup;
    13 
    14             // Sort by last modification date/loader order
    15             return firstNonEmptyGroup
    16                 .OrderByDescending(probe => GetVirtualPathDepedenciesModificationTimeUtc(virtualPathModficationDates, probe))
    17                 .ThenBy(probe => probe.Loader.Order)
    18                 .ToList();
    19         }
    20 private DateTime GetVirtualPathDepedenciesModificationTimeUtc(ConcurrentDictionary<string, DateTime> virtualPathDependencies, ExtensionProbeEntry probe) {
    21             if (!probe.VirtualPathDependencies.Any())
    22                 return DateTime.MinValue;
    23 
    24             Logger.Information("Retrieving modification dates of dependencies of extension '{0}'", probe.Descriptor.Id);
    25 
    26             var result = probe.VirtualPathDependencies.Max(path => GetVirtualPathModificationTimeUtc(virtualPathDependencies, path));
    27 
    28             Logger.Information("Done retrieving modification dates of dependencies of extension '{0}'", probe.Descriptor.Id);
    29             return result;
    30         }
    31 
    32         private DateTime GetVirtualPathModificationTimeUtc(ConcurrentDictionary<string, DateTime> virtualPathDependencies, string path) {
    33             return virtualPathDependencies.GetOrAdd(path, p => _virtualPathProvider.GetFileLastWriteTimeUtc(p));
    34         }
    35 public virtual DateTime GetFileLastWriteTimeUtc(string virtualPath) {
    36 #if true
    37             // This code is less "pure" than the code below, but performs fewer file I/O, and it 
    38             // has been measured to make a significant difference (4x) on slow file systems.
    39             return File.GetLastWriteTimeUtc(MapPath(virtualPath));
    40 #else
    41             var dependency = HostingEnvironment.VirtualPathProvider.GetCacheDependency(virtualPath, new[] { virtualPath }, DateTime.UtcNow);
    42             if (dependency == null) {
    43                 throw new Exception(string.Format("Invalid virtual path: '{0}'", virtualPath));
    44             }
    45             return dependency.UtcLastModified;
    46 #endif
    47         }
    View Code

    探测引用:

      引用探测是探测阶段的最后一步,它将查找所有模块的引用信息。

    1 .RunInParallel(availableExtensions, extension => _loaders.SelectMany(loader => loader.ProbeReferences(extension)).ToList())

          1. CoreExtensionLoader:基类的ProbeReferences方法将被调用,返回一个空集合。

    1      public virtual IEnumerable<ExtensionReferenceProbeEntry> ProbeReferences(ExtensionDescriptor descriptor) {
    2             return Enumerable.Empty<ExtensionReferenceProbeEntry>();
    3         }

          2. DynamicExtensionLoader:通过解析csproj文件获取的reference信息,创建ExtensionReferenceProbeEntry。

     1         public override IEnumerable<ExtensionReferenceProbeEntry> ProbeReferences(ExtensionDescriptor descriptor) {
     2             if (Disabled)
     3                 return Enumerable.Empty<ExtensionReferenceProbeEntry>();
     4 
     5             Logger.Information("Probing references for module '{0}'", descriptor.Id);
     6 
     7             string projectPath = GetProjectPath(descriptor);
     8             if (projectPath == null)
     9                 return Enumerable.Empty<ExtensionReferenceProbeEntry>();
    10 
    11             var projectFile = _projectFileParser.Parse(projectPath);
    12 
    13             var result = projectFile.References.Select(r => new ExtensionReferenceProbeEntry {
    14                 Descriptor = descriptor,
    15                 Loader = this,
    16                 Name = r.SimpleName,
    17                 VirtualPath = _virtualPathProvider.GetProjectReferenceVirtualPath(projectPath, r.SimpleName, r.Path)
    18             });
    19 
    20             Logger.Information("Done probing references for module '{0}'", descriptor.Id);
    21             return result;
    22         }

          3. PrecompiledExtensionLoader:找到模块程序集路径,将该路径下的所有除了模块程序集以外的.dll文件全部获取路径,并创建ExtensionReferenceProbeEntry。

     1         public override IEnumerable<ExtensionReferenceProbeEntry> ProbeReferences(ExtensionDescriptor descriptor) {
     2             if (Disabled)
     3                 return Enumerable.Empty<ExtensionReferenceProbeEntry>();
     4 
     5             Logger.Information("Probing references for module '{0}'", descriptor.Id);
     6 
     7             var assemblyPath = GetAssemblyPath(descriptor);
     8             if (assemblyPath == null)
     9                 return Enumerable.Empty<ExtensionReferenceProbeEntry>();
    10 
    11             var result = _virtualPathProvider
    12                 .ListFiles(_virtualPathProvider.GetDirectoryName(assemblyPath))
    13                 .Where(s => StringComparer.OrdinalIgnoreCase.Equals(Path.GetExtension(s), ".dll"))
    14                 .Where(s => !StringComparer.OrdinalIgnoreCase.Equals(Path.GetFileNameWithoutExtension(s), descriptor.Id))
    15                 .Select(path => new ExtensionReferenceProbeEntry {
    16                     Descriptor = descriptor,
    17                     Loader = this,
    18                     Name = Path.GetFileNameWithoutExtension(path),
    19                     VirtualPath = path
    20                 } )
    21                 .ToList();
    22 
    23             Logger.Information("Done probing references for module '{0}'", descriptor.Id);
    24             return result;
    25         }

        4. RawThemeExtensionLoader:使用基类方法,获取空列表。
        5. ReferencedExtensionLoader:使用基类方法,获取空列表。

      这里要说明一下,因为Core模块和被引用的模块是被根目录直接引用的,所以在编译时会将模块所依赖的库一并复制到根项目的bin目录中,所以无需额外处理它们。预编译和动态编译的模块的引用信息分别从bin目录下或csproj文件中提取。
    引用后续的处理就是根据模块和引用名称进行了分组,以便后续使用。

    小结:以上过程主要目的是为了创建一个ExtensionLoadingContext用于后续加载。

    1 return new ExtensionLoadingContext {
    2                 AvailableExtensions = sortedAvailableExtensions,
    3                 PreviousDependencies = previousDependencies,
    4                 DeletedDependencies = deletedDependencies,
    5                 AvailableExtensionsProbes = availableExtensionsProbes,
    6                 ReferencesByName = referencesByName,
    7                 ReferencesByModule = referencesByModule,
    8                 VirtualPathModficationDates = virtualPathModficationDates,
    9 }
  • 相关阅读:
    保持简单----纪念丹尼斯•里奇(Dennis Ritchie)
    转:有关retina和HiDPI那点事
    Powershell 学习
    Windows与Linux共享文件夹互相访问
    你知道C语言为什么会有“_”(下划线)吗?
    百度公共DNS
    AXIS2的一些认识
    待整理
    java复习汇总之面试篇
    落网歌曲图片下载
  • 原文地址:https://www.cnblogs.com/selimsong/p/6085181.html
Copyright © 2011-2022 走看看