您的位置:首页 > 其它

Orchard源码分析(5.1):Host初始化(DefaultOrchardHost.Initialize方法)

2012-10-18 11:48 337 查看
概述Orchard作为一个可扩展的CMS系统,是由一系列的模块(Modules)或主题(Themes)组成,这些模块或主题统称为扩展(Extensions)。在初始化或运行时需要对扩展进行安装:DefaultOrchardHost.SetupExtensions方法。当添加新的扩展、删除扩展或修改扩展源码后,需要通知扩展加载器(Extension Loader)重新加载或完成一些清理工作,所以需要进行监视:DefaultOrchardHost.MonitorExtensions方法。Orchard 是一个多租户(Tenant)系统,也就是我们通常所是说的子站点,它允许一个Orchard应用程序中包含多个不同域名的子站点。每个子站点对应一个 Shell,从源码角度上看,Host对应的是IOrchardShell接口及其实现DefaultOrchardShell类。在Orchard启动 或运行时,需要创建并激活:DefaultOrchardHost.CreateAndActivateShells方法。在调用DefaultOrchardHost.Initialize方法进行初始化时,会通过调用BuilderCurrent方法间接顺序调用上述三个方法。请看BuildCurrent方法的源码:[align=left] IEnumerable<ShellContext > BuildCurrent() {[/align][align=left] if (_shellContexts == null ) {[/align][align=left] lock (_syncLock) {[/align][align=left] if (_shellContexts == null ) {[/align][align=left] SetupExtensions();[/align][align=left] MonitorExtensions();[/align][align=left] CreateAndActivateShells();[/align][align=left] }[/align][align=left] }[/align][align=left] }[/align][align=left]
[/align][align=left] return _shellContexts;[/align][align=left] }[/align][align=left][/align][align=left]这 里有个对_shellContexts是否为null的判断,以避免重复安装扩展。CreateAndActivateShells方法成功激活 Shell后,_shellContexts将不会为null。关于ShellContext,在会在后面Shell相关的文章中有所分析。[/align][align=left]由于BuildCurrent方法并不会只供Initialize方法调用,还可能被BeginRequest事件处理方法调用,所以lock住以保证线程安全。[/align][align=left] [/align]一、安装扩展:SetupExtensions方法DefaultOrchardHost.SetupExtensions 方法实际上是调用的ExtensionLoaderCoordinator.SetupExtensions方法,在下文中提到 SetupExtensions指的就是后者。通过字面上看ExtensionLoaderCoordinator可以叫做"扩展加载器的协调器"。
安装扩展包括这些步骤:1、创建扩展加载上下文(ExtensionLoadingContext)SetupExtensions方法会调用私有的CreateLoadingContext方法获取可用的扩展:[align=left] var availableExtensions = _extensionManager[/align][align=left] .AvailableExtensions()[/align][align=left] .Where(d => DefaultExtensionTypes.IsModule(d.ExtensionType) || DefaultExtensionTypes .IsTheme(d.ExtensionType))[/align][align=left] .OrderBy(d => d.Id)[/align][align=left] .ToList();[/align][align=left] [/align]_extensionManager是扩展管理器ExtensionManager,由它的AvailableExtensions方法来调度扩展的搜索和收集(Harvest): //以下代码来自ExtensionManager类[align=left] public IEnumerable <ExtensionDescriptor> AvailableExtensions() {[/align][align=left] return _cacheManager.Get("AvailableExtensions" , ctx =>[/align][align=left] _parallelCacheContext[/align][align=left] .RunInParallel(_folders, folder => folder.AvailableExtensions().ToList())[/align][align=left] .SelectMany(descriptors => descriptors)[/align][align=left] .ToReadOnlyCollection());[/align][align=left] }[/align][align=left]
[/align][align=left]ExensionManager采用并行(Parallel)的方式以提高效率,但可以通过在配置文件HostComponents.config中设置Orchard.Caching.DefaultParallelCacheContext类型的Disabled属性为false以禁用并行机制。[/align] [align=left]_folders是一个集合,它包含ModuleFolders、CoreModuleFolders和ThemeFolders型的三个对象。通过上面的代码看起来是这三个类在负责搜索和收集工作。实际上这三个类会调用扩展收集器ExtensionHarvester的HarvestExtensions方法来做实际的工作。该方法根据不同的参数在~/Modules、~/Core和~/Themes三个目录的所有一级子目录中搜索Module.txt和Theme.txt文件。每一个Module.txt或Theme.txt文件都会被反序列化成一个扩展描述ExtensionDescriptor对象。扩展描述对象包括扩展名称、所在路径、分类、作者和扩展ID等信息,其中扩展ID在下面的分析中经常被用到,它指扩展目录的名称,比如~/Modules/Orchard.Blogs的扩展ID是"Orchard.Blogs"。[/align][align=left][/align][align=left]不难看出SetupExtensions方法中的availableExtensions是一个按扩展ID排序后的List<ExtensionDescriptor>型的集合,这里称为扩展描述集合。依赖于这个集合,来创建一个扩展加载上下文ExtensionLoadingContext对象。[/align]扩展加载上下文类主要定义了一些属性,这些的属性虽不多,但意义复杂:(1)、PreviousDependencies:List<DependencyDescriptor>型。扩展依赖描述DependencyDescriptor 对象的集合,是通过DefaultDependenciesFolder类对~/App_Data/Dependencies /dependencies.xml文件反序列化而来。如果是第一次加载扩展,该集合当然就会为null。[align=left] var previousDependencies = _dependenciesFolder.LoadDescriptors().ToList();[/align][align=left]
[/align]扩展依赖描述是包含了扩展的名称、扩展加载器名称、扩展的虚拟路径和"扩展所引用的其他扩展"的描述DependencyReferenceDescriptor对象集合:[align=left] public class DependencyDescriptor {[/align][align=left] public DependencyDescriptor() {[/align][align=left] References = Enumerable.Empty<DependencyReferenceDescriptor >();[/align][align=left] }[/align][align=left] public string Name { get; set; }[/align][align=left] public string LoaderName { get; set; }[/align][align=left] public string VirtualPath { get; set; }[/align][align=left] public IEnumerable <DependencyReferenceDescriptor> References { get; set ; }[/align][align=left] }[/align][align=left]
[/align][align=left] public class DependencyReferenceDescriptor {[/align][align=left] public string Name { get; set; }[/align][align=left] public string LoaderName { get; set; }[/align][align=left] public string VirtualPath { get; set; }[/align][align=left] }[/align][align=left][/align][align=left]其中Name为扩展的名称,和上面提到的扩展ID是相同的;LoaderName为扩展加载器名称,这后面会详细介绍;VirtualPath为扩展的虚拟路径,可能是目录路径或文件路径——这要看是被哪个扩展加载器加载。[/align][align=left] [/align]PreviousDependencies属性实际上是冗余的。而previousDependencies下面会用到。(2)、DeletedDependencies: List<DependencyDescriptor>型。最后一次在系统中成功被加载的、但当前已经被移除的扩展的扩展依赖描述。是上面搜索得到的扩展描述集合availableExtensions与previousDependencies比较筛选而来,用于在后面的操作中可以删除扩展相关的程序集。如果没有对扩展进行过删除操作,该集合当然就会为空。(3)、AvailableExtensionsProbes:IDictionary<string, IEnumerable<ExtensionProbeEntry>>型。按扩展ID分组的的扩展探测条目ExtensionProbeEntry对象集合。扩展ID前面提到过,实际上就是扩展的目录名称。扩展探测条目由扩展加载器探测(Probe)而来。具体某一个扩展ID对应的扩展探测条目集合(IEnumerable<ExtensionProbeEntry>)是按探测条目优先级(Priority)、"扩展依赖的虚拟路径的最后修改时间"倒序、扩展加载器Order属性三个条件排序的集合。排序算法请看ExtensionLoaderCoordinator类的SortExtensionProbeEntries方法。生成该属性的值有三个步骤,探测(Probe)、分组(Group)和排序(Sort)。Orchard提供了5个扩展加载器:CoreExtensionLoader,ReferencedExtensionLoader,PrecompiledExtensionLoader,DynamicExtensionLoader,RawThemeExtensionLoader。这些扩展器探对根据扩展描述探测出的"扩展探测条目"的数量,以及"扩展探测条目"优先级(Priority)也不尽相同:CoreExtensionLoader只会探测~/Core目录下的扩展;ReferencedExtensionLoader会探测引用了外部程序集的扩展;PrecompiledExtensionLoader会探测扩展目录下的bin子目录有"<扩展ID>.dll"的扩展;DynamicExtensionLoader会探测在扩展目录下有C#项目文件"<扩展ID>.csproj"的扩展;RawThemeExtensionLoader只会探测~/Themes目录下的扩展(即主题)。该加载器还比较特殊,如果在主题目录下有C#项目文件"<主题ID>.csproj"或者主题目录下的bin子目录有"<主题名称>.dll",就不会继续使用该扩展探测器进行探测。注 意:一个扩展虽然会被5个扩展加载器探测,但并不一定会获取5个扩展探测条目,并且扩展最终只会被一个扩展加载器进行加载。所以扩展探测条目集合的顺序至 关重要,它是决定用哪一个扩展加载器来加载对应的扩展的重要因素(另外一个因素是当扩展引用了其他的扩展,被引用的扩展使用的是 DynamicExtensionLoader加载,则前者不能采用PrecompiledExtensionLoader来加载,您可以想想这是为什 么)。扩展描述ExtensionDescriptor对应的若干条扩展探测条目被探测出,再根据扩展ID进行分组:[align=left] var availableExtensionsProbes1 = _parallelCacheContext[/align][align=left] .RunInParallel(availableExtensions, extension =>[/align][align=left] _loaders.Select(loader => loader.Probe(extension)).Where(entry => entry != null).ToArray())[/align][align=left] .SelectMany(entries => entries)[/align][align=left] .GroupBy(entry => entry.Descriptor.Id);[/align][align=left] [/align]然后根据探测条目优先级(Priority)、"扩展依赖的虚拟路径的最后修改时间"及扩展加载器的Order属性排序:[align=left] var availableExtensionsProbes = _parallelCacheContext[/align][align=left] .RunInParallel(availableExtensionsProbes1, g =>[/align][align=left] new { Id = g.Key, Entries = SortExtensionProbeEntries(g, virtualPathModficationDates)})[/align][align=left] .ToDictionary(g => g.Id, g => g.Entries, StringComparer.OrdinalIgnoreCase);[/align][align=left] [/align]排序算法在SortExtensionProbeEntries方法进行。探测条目优先级是指ExtensionProbeEntry的Priority属性。五个扩展加载器探测出的条目优先级:CoreExtensionLoader 扩展探测条目优先级为100;ReferencedExtensionLoader 扩展探测条目优先级为100;PrecompiledExtensionLoader 扩展探测条目优先级为0;DynamicExtensionLoader会 扩展探测条目优先级为0;RawThemeExtensionLoader 扩展探测条目优先级为0。
"扩展依赖的虚拟路径"是指ExtensionProbeEntry的VirtualPathDependencies属性,是一个字符串集合。请注意它并不是指扩展的虚拟路径。不同的扩展加载器对"扩展依赖的虚拟路径"的理解是不一样的:CoreExtensionLoader:无扩展依赖虚拟路径ReferencedExtensionLoader:~/bin/<扩展ID>.dll文件PrecompiledExtensionLoader:~/<Core、Modules或Themes>/<扩展ID>/bin/<扩展ID>.dll文件DynamicExtensionLoader:首先将~/<Core、Modules或Themes>/<扩展ID>/<扩展ID>.csproj反序列化(该文件就是一XML文件)再从中提取。扩展依赖路径就包含四部分:一是".csproj"文件本身;二是扩展项目包含的".cs"等源文件;三是扩展项目引用的第三方程序集;四是扩展项目引用的其他扩展(仅~/Modules和~/Theme目录下的扩展)的路径,这会引起递归搜索。RawThemeExtensionLoader:无扩展依赖虚拟路径不同的扩展加载器的Order属性值也是不一样的:CoreExtensionLoader:10ReferencedExtensionLoader:20PrecompiledExtensionLoader:30DynamicExtensionLoader:100RawThemeExtensionLoader:10
(4)、ReferencesByName:IDictionary<string, IEnumerable<ExtensionReferenceProbeEntry>>型。按扩展引用探测条目所属的扩展ID(ExtensionDescriptor的Id属性)分组的扩展引用探测条目ExtensionReferenceProbeEntry集合。与"扩展探测条目"类似,"扩展引用探测条目"由也是由扩展加载器探测而来,只有PrecompiledExtensionLoader和DynamicExtensionLoader这两种探测器有实际的功能。PrecompiledExtensionLoader将探测出~/<Core、Modules或Themes>/<扩展ID>/bin/<扩展ID>.dll,即扩展项目引用的其他程序集。该扩展加载器探测出来的"扩展引用探测条目(ExtensionReferenceProbeEntry)"的Name属性值为"<扩展ID>.dll"文件的不包含扩展名的文件名。DynamicExtensionLoader 将探测出~/<Core、Modules或Themes>/<扩展ID>/<扩展ID>.csproj文件中描述 的、扩展项目引用的其他程序集或其他项目。该扩展加载器探测出来的"扩展引用探测条目(ExtensionReferenceProbeEntry)"的 Name属性值,如果是程序集则程序集的短名称,如果是源程序项目则为项目"<项目名称>.csproj"文件的不包含扩展名的文件名。[align=left] var references = _parallelCacheContext[/align][align=left] .RunInParallel(availableExtensions, extension => _loaders.SelectMany(loader => loader.ProbeReferences(extension)).ToList())[/align][align=left] .SelectMany(entries => entries)[/align][align=left] .ToList();[/align][align=left] //......[/align][align=left] var referencesByName = references[/align][align=left] .GroupBy(reference => reference.Descriptor.Id, StringComparer.OrdinalIgnoreCase)[/align][align=left] .ToDictionary(g => g.Key, g => g.AsEnumerable(), StringComparer.OrdinalIgnoreCase);[/align][align=left] [/align](5)、ReferencesByModule:IDictionary<string, IEnumerable<ExtensionReferenceProbeEntry>>型。按扩展引用探测条目名称(ExtensionReferenceProbeEntry的Name属性)分组的扩展引用探测条目集合。ReferencesByModule与ReferenceByName类似,唯一的不同就是分组方式。[align=left]
[/align][align=left] var referencesByModule = references[/align][align=left] .GroupBy(entry => entry.Name, StringComparer .OrdinalIgnoreCase)[/align][align=left] .ToDictionary(g => g.Key, g => g.AsEnumerable(), StringComparer .OrdinalIgnoreCase);[/align][align=left] [/align](6)、VirtualPathModficationDates:ConcurrentDictionary<string, DateTime>型。扩展对应的"扩展依赖虚拟路径最后修改时间"字典。该属性是冗余的。(7)、AvailableExtensions:List<ExtensionDescriptor>型。当前可用的扩展描述(ExtensionDescriptor型)对象集合。该集合是上面获取的availableExtensions集合通过扩展ID(也就是扩展的目录名称)和扩展之间的依赖关系两个条件排序而来,这类似于Vistual Studio解决方案的按"项目生成顺序"显示的列表。[align=left] var sortedAvailableExtensions =[/align][align=left] availableExtensions.OrderByDependenciesAndPriorities([/align][align=left] (item, dep) => referencesByModule.ContainsKey(item.Id) &&[/align][align=left] referencesByModule[item.Id].Any(r => StringComparer.OrdinalIgnoreCase.Equals(dep.Id, r.Name)),[/align][align=left] item => 0)[/align][align=left] .ToList();[/align][align=left] [/align]2、从扩展加载上下文(Extension Loading Context)获取已经被移除的扩展,并通知扩展对应的扩展加载器(Extension Loader)[align=left] foreach (var dependency in context.DeletedDependencies) {[/align][align=left] Logger.Information( "Extension {0} has been removed from site" , dependency.Name);[/align][align=left] foreach (var loader in _loaders) {[/align][align=left] if (dependency.LoaderName == loader.Name) {[/align][align=left] loader.ExtensionRemoved(context, dependency);[/align][align=left] }[/align][align=left] }[/align][align=left] }[/align][align=left] [/align]如 上所述,扩展加载上下文的DeletedDependencies属性保存了已经被删除的扩展的依赖描述信息。扩展之前被哪个扩展加载器所成功加载,就交 由它来定义相应的删除动作(Delete Action)。删除动作实际上是一系列的方法,他们将会被保存在扩展加载上下文的DeleteActions属性中,该属性是一个Action委托集合。也就是说,在这里并不会进行真正的清理工作。下面列出五个扩展加载器定义的删除动作:CoreExtensionLoader:~/Core下的模块总是需要被加载,所以没有定义删除动作。ReferencedExtensionLoader:删除~/bin/<扩展ID>.dll文件PrecompiledExtensionLoader:删除~/App_Data/Dependencies/<扩展ID>.dll文件DynamicExtensionLoader:无操作RawThemeExtensionLoader:无操作程序集可能位于~/Bin或~/App_Data/Dependencies目录。删除~/Bin目录的程序集会设置"重启应用程序域标记"为true;删除~/App_Data/Dependencies目录的程序集会判断程序集是否已经被应用程序域加载,如果已经加载则设置"重启应用程序域标记"为true。注 意,对于PrecompiledExtensionLoader和DynamicExtensionLoader这两个扩展加载器在激活扩展的时候,如果 扩展引用了第三方程序集,也会被复制到~/App_Data/Dependencies目录中。但是在删除扩展的时候,并不会删除这些程序集。因为 Orchard还无法判断除了相应扩展之外,是否还有另外的扩展也引用了这些程序集。这意味着有可能需要手动到~/App_Data /Dependencies目录去删除确定不会用到的程序集。3、扩展加载器激活扩展(Activate Extensions)[align=left] foreach (var extension in context.AvailableExtensions) {[/align][align=left] ProcessExtension(context, extension);[/align][align=left] }[/align][align=left][/align]扩展加载器可能会把扩展需要用到的程序集的复制操作(Copy Action)放入扩展加载器上下文中的CopyActions属性中,该属性是一个Action委托集合。就是说,在这里并不会进行真正的复制工作。下面列出五个扩展加载器定义的复制动作:CoreExtensionLoader:~/Core下的模块对应的Orchard.Core.dll已经被放在了~/Bin目录下,所以没有定义复制动作。ReferencedExtensionLoader:程序集右键被放在了~/Bin目录下,也没有定义复制操作。PrecompiledExtensionLoader:复制~/<Core、Modules或Themes>/<扩展ID>/bin/<扩展ID>.dll文件到~/App_Data/Dependencies目录。如果扩展引用了第三方程序集,也会进行复制。DynamicExtensionLoader:如果扩展引用了第三方程序集,则将这些程序集复制到~/App_Data/Dependencies目录。RawThemeExtensionLoader:无操作
注意:(1)、扩展有可能引用了其他扩展,但是被引用的扩展总是会先被激活。(2)、DynamicExtensionLoader扩展加载器并不会在这里对相关扩展进行动态编译。4、执行程序集复制或删除操作[align=left] ProcessContextCommands(context);[/align][align=left] [/align]根据扩展加载上下文的DeleteActions属性和CopyActions属性,完成程序集的删除或复制操作: private void ProcessContextCommands(ExtensionLoadingContext ctx) {[align=left] Logger.Information( "Executing list of operations needed for loading extensions..." );[/align][align=left] foreach (var action in ctx.DeleteActions) {[/align][align=left] action();[/align][align=left] }[/align][align=left]
[/align][align=left] foreach (var action in ctx.CopyActions) {[/align][align=left] action();[/align][align=left] }[/align][align=left] }[/align][align=left] [/align]5、将扩展依赖信息存储到xml文件中[align=left] _dependenciesFolder.StoreDescriptors(context.NewDependencies);[/align][align=left] _extensionDependenciesManager.StoreDependencies(context.NewDependencies, desc => GetExtensionHash(context, desc));[/align][align=left] [/align]将所有扩展的依赖关系写入到~/App_Data/Dependencies/dependencies.xml文件中。另,还会写入~/App_Data/Dependencies/dependencies_compiled.xml文件中,这里暂不关注。6、如有必要,重启应用程序域[align=left] if (context.RestartAppDomain) {[/align][align=left] Logger.Information( "AppDomain restart required." );[/align][align=left] _hostEnvironment.RestartAppDomain();[/align][align=left] }[/align][align=left] [/align]如果"重启应用程序域标记"RestartAppDomain为true,则会重启应用程序域。7、总结说明提 取几个重要细节进行总结说明:搜索扩展(Look for Extensions)、探测扩展(Probe Extensions)、获取已经被移除的扩展的依赖、检测扩展引用(Probe Extension References)、激活扩展(Activate Extensions)。(1)、搜索扩展关于扩展搜索上文已有描述,在~/Modules、~/Core和~/Themes三个目录的所有一级子目录中搜索Module.txt和Theme.txt文件。每一个Module.txt或Theme.txt文件都会被反序列化成扩展描述ExtensionDescriptor对象,组合成一个按扩展目录名称排序后的List<ExtensionDescriptor>对象以供后面使用。然后根据目录名称判断是否有重复的扩展,如果有相同名称的扩展则会报异常。比如在~/Modules目录有个Lucene扩展,在~/Core或~/Themes就不能有相同目录名称的扩展。有必要说一下,因为扩展加载器(Extension Loader)的原因,请务必保证扩展的程序集名称扩展目录名称一致。当新建一个扩展项目的时候默认就是这样,也就是说不要去修改它。(2)、探测扩展不同的扩展加载器有不同的探测策略,关于这方面上文已有比较详细的描述。(3)、获取已经被移除的扩展的依赖我们知道,不管是Module还是Theme的扩展,都是独立的项目,项目除了有自己程序集,还可能依赖第三方程序集或其他扩展项目。~/App_Data/Dependencies/dependencies.xml扩展依赖文件保存了最后一次成功加载扩展时,扩展、扩展加载器和扩展所引用的程序集列表。读取扩展依赖文件将之反序列化最终为一个List<DependencyDescriptor>对象(依赖描述)。在SetupExtensions方法中,会比较依赖描述和本次查找而来的扩展,如果检测到某些扩展已经被移除,则相关的程序集dll文件也应该被移除。dll移除工作由对应的扩展加载器配合完成。(4)、探测扩展引用实 际上只有PrecompiledExtensionLoader和DynamicExtensionLoader这两种探测器有实际的功能。 PrecompiledExtensionLoader将提取在“~/<Core、Modules或Themes>/<扩展 ID>/bin”目录下“<扩展ID>.dll”的程序集;DynamicExtensionLoader将提取在 “~/<Modules或Themes>/<扩展ID>.csproj”项目文件描述的引用的第三方程序集或其他扩展项目的程序 集(PrecompiledExtensionLoader不会对~/Core扩展目录的核心扩展进行探测)。(5)、激活扩展Orchard提供了5个扩展加载器,针对不同的加载、激活策略:CoreExtensionLoader如 果“Module.txt”文件来自于"~/Core"文件夹,CoreModuleLoader将返回来自于Orchard.Core.dll 中,"Orchard.Core.<扩展ID>"命名空间下的所有类型。Orchard.Core.dll是一个特殊的程序集,它包含了 Orchard核心模块,在Orchard框架基础上提供一些基本功能。ReferencedExtensionLoader在 “~/bin”目录中查找“Module.txt”文件中模块名对应的程序集,如果这个程序集存在,它将加载并返回改程序集的所有类型。这种加载机用于当 所有模块都是预先编译好的,并且其dll都存储在“~/bin”目录中的情况,这是一种典型“ASP.NET MVC”应用程序方式。PrecompiledExtensionLoader如果“Module.txt”文件来自于"~/Core"、"~ /Modules"、"~/Themes" 文件夹,PrecompiledExtensionLoader将在“~/<Core、Modules 或Themes>/<扩展ID>/bin”中查找名为<扩展ID>.dll的程序集。如果这个文件存在,它将被复制到"~ /App_Data/Dependencies"文件夹下,这是一个特殊的文件夹,在~/Web.config文件中配置进行过配置,是用于 ASP.NET查找“~/bin”文件夹以外程序集的地方。DynamicExtensionLoader如 果“Module.txt”文件来自于"~/Core"、"~/Modules"、"~/Themes" 文件夹,DynamicExtensionLoader将在“~/<Core、Modules或Themes>/<扩展ID>” 目录中查找.csproj文件。如果这个文件存在,这个加载器将使用Orchard编译管理器根据.csproj文件来编译程序集并返回改程序集的所有类 型——Orchard中扩展的动态编译指的就是这部分了。RawThemeExtensionLoader如 果“Module.txt”文件来自于"~/Themes" 文件夹,则由RawThemeExtensionLoader负责加载。其实基本上不做什么事情,因为如果一个模块有程序集或.csproj文件,就轮不 到它,而是交给PrecompiledExtensionLoader或DynamicExtensionLoader来处理了。二、监视扩展:MonitorExtensions方法如《IIS 5.0 和 6.0 的 ASP.NET 应用程序生命周期概述》所述,某些操作将导致应用程序重新启动。除此之外,包括扩展改变在内的一些其他操作也将导致Orchard重启。为了做到这一点,Orchard通过调用MonitorExtensions方法监视扩展的变化。首先会监视~/Modules和~/Themes目录的最后修改时间(扩展的增删操作)的变化。通过扩展加载器还会对扩展进行监视:CoreExtensionLoader:不监视,Orchard.Core.dll在~/Bin目录中,有变动自然会重启。ReferencedExtensionLoader:不监视,因为程序集也是放在~/Bin目录PrecompiledExtensionLoader:监视~/<Core、Modules或Themes>/<扩展ID>/bin/<扩展ID>.dll文件(如果存在的话),以及~/<Core、Modules或Themes>/<扩展ID>/bin目录。DynamicExtensionLoader:监视~/<Core、Modules或Themes>/<扩展ID>/<扩展ID>.csproj文件和所有.cs文件,以及.csproj文件中描述的项目引用(会引起递归搜索)RawThemeExtensionLoader:不监视,改改UI就重启那受不了。需要注意一点,尽管一个扩展是由一个扩展加载器加载,却受多个扩展加载器监视。
最后会监视~/App_Data/hrestart.txt最后修改时间的变化。
MonitorExtensions方法会在Orchard初始化和每次处理BeginRequest事件的时候得以执行。初始化时执行该方法的目的在于处理一种边界情况,那就是在扩展被探测完的时候正进行安装扩展的时候,如果扩展有扩展增删改的操作,能够及时进行某些处理。运行时处理BeginRequest事件的时候,也会调用该方法监视到扩展的变化。另,在分析Orchard.Caching.CacheModule的时候,也提到过监视缓存到期的机制。适当的时候我们专门就"监视"功能进行分析。三、创建和激活Shell:CreateAndActivateShells方法首 先由ShellSettingsManager从~/App_Data/Sites目录的子目录中搜索Settings.txt文件,并将其反序列化为 ShellSettings对象。一系列ShellSettings对象形成一个集合。如果ShellSettings对象集合不为空,则使用 ShellContextFactory.CreateShellContext为每个ShellSettings对象创建对应的 ShellContext对象,否则使用ShellContextFactory.CreateSetupContext创建一个安装上下文 ShellContext对象。针对具体的ShellContext,调用其包含的Shell(DefaultOrchardShell)的Activate方法进行激活。另外,会对ShellContext对象集合进行缓存,当需要重新安装扩展时,只需要将其设置null,在处理下一次BeginRequest事件时就能够重新启动安装扩展的操作。BuildCurrent方法进行该判断。关于Shell,我们有专门的篇幅来介绍,包括Shell是什么及其作用、更详细创建及激活操作分析。相关类型:Orchard.Environment.DefaultOrchardHost : IOrchardHostOrchard.Environment.Extensions.ExtensionLoadingContext
Orchard.Environment.Extensions.ExtensionLoaderCoordinator : IExtensionLoaderCoordinatorOrchard.Environment.Extensions.ExtensionManager : IExtensionManagerOrchard.Environment.Extensions.Folders.ExtensionHarvester : IExtensionHarvesterOrchard.Environment.Extensions.Folders.ModuleFolders: IExtensionFoldersOrchard.Environment.Extensions.Folders.CoreModuleFolders: IExtensionFoldersOrchard.Environment.Extensions.Folders.ThemeModuleFolders: IExtensionFoldersOrchard.FileSystems.Dependencies.DefaultDependenciesFolder:IDependenciesFolderOrchard.Environment.Extensions.Models.ExtensionDescriptorOrchard.Environment.Extensions.Loaders.ExtensionProbeEntryOrchard.Environment.Extensions.Loaders.ExtensionReferenceProbeEntryOrchard.FileSystems.Dependencies.DependencyDescriptorOrchard.FileSystems.Dependencies.DependencyReferenceDescriptorOrchard.Environment.Extensions.ExtensionEntryOrchard.Environment.Extensions.ExtensionMonitoringCoordinator : IExtensionMonitoringCoordinatorOrchard.Environment.Configuration.ShellSettingsOrchard.Environment.Configuration.ShellSettingsManager: IShellSettingsManagerOrchard.Environment.ShellBuilders.ShellContextOrchard.Environment.ShellBuilders.ShellContextFactory: IShellContentFactoryOrchard.Environment.ShellBuilders.ShellContainerFactoryOrchard.Environment.ShellBuilders.CompositionStrategy: ICompositionStrategyOrchard.Caching.DefaultParallelCacheContext参考资料:
Orchard module loader and dynamic compilationOrchard动态编译机制(上文翻译)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: