概述
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方法的源码:
IEnumerable<ShellContext > BuildCurrent() {
if (_shellContexts == null ) {
lock (_syncLock) {
if (_shellContexts == null ) {
SetupExtensions();
MonitorExtensions();
CreateAndActivateShells();
}
}
}
return _shellContexts;
}
这 里有个对_shellContexts是否为null的判断,以避免重复安装扩展。CreateAndActivateShells方法成功激活 Shell后,_shellContexts将不会为null。关于ShellContext,在会在后面Shell相关的文章中有所分析。
由于BuildCurrent方法并不会只供Initialize方法调用,还可能被BeginRequest事件处理方法调用,所以lock住以保证线程安全。
一、安装扩展:SetupExtensions方法
DefaultOrchardHost.SetupExtensions 方法实际上是调用的ExtensionLoaderCoordinator.SetupExtensions方法,在下文中提到 SetupExtensions指的就是后者。通过字面上看ExtensionLoaderCoordinator可以叫做"扩展加载器的协调器"。
安装扩展包括这些步骤:
1、创建扩展加载上下文(ExtensionLoadingContext)
SetupExtensions方法会调用私有的CreateLoadingContext方法获取可用的扩展:
var availableExtensions = _extensionManager
.AvailableExtensions()
.Where(d => DefaultExtensionTypes.IsModule(d.ExtensionType) || DefaultExtensionTypes .IsTheme(d.ExtensionType))
.OrderBy(d => d.Id)
.ToList();
_extensionManager是扩展管理器ExtensionManager,由它的AvailableExtensions方法来调度扩展的搜索和收集(Harvest):
//以下代码来自ExtensionManager类
public IEnumerable <ExtensionDescriptor> AvailableExtensions() {
return _cacheManager.Get("AvailableExtensions" , ctx =>
_parallelCacheContext
.RunInParallel(_folders, folder => folder.AvailableExtensions().ToList())
.SelectMany(descriptors => descriptors)
.ToReadOnlyCollection());
}
ExensionManager采用并行(Parallel)的方式以提高效率,但可以通过在配置文件HostComponents.config中设置Orchard.Caching.DefaultParallelCacheContext类型的Disabled属性为false以禁用并行机制。
_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"。
不难看出SetupExtensions方法中的availableExtensions是一个按扩展ID排序后的List<ExtensionDescriptor>型的集合,这里称为扩展描述集合。依赖于这个集合,来创建一个扩展加载上下文ExtensionLoadingContext对象。
扩展加载上下文类主要定义了一些属性,这些的属性虽不多,但意义复杂:
(1)、PreviousDependencies:List<DependencyDescriptor>型。扩展依赖描述DependencyDescriptor 对象的集合,是通过DefaultDependenciesFolder类对~/App_Data/Dependencies /dependencies.xml文件反序列化而来。如果是第一次加载扩展,该集合当然就会为null。
var previousDependencies = _dependenciesFolder.LoadDescriptors().ToList();
扩展依赖描述是包含了扩展的名称、扩展加载器名称、扩展的虚拟路径和"扩展所引用的其他扩展"的描述DependencyReferenceDescriptor对象集合:
public class DependencyDescriptor {
public DependencyDescriptor() {
References = Enumerable.Empty<DependencyReferenceDescriptor >();
}
public string Name { get; set; }
public string LoaderName { get; set; }
public string VirtualPath { get; set; }
public IEnumerable <DependencyReferenceDescriptor> References { get; set ; }
}
public class DependencyReferenceDescriptor {
public string Name { get; set; }
public string LoaderName { get; set; }
public string VirtualPath { get; set; }
}
其中Name为扩展的名称,和上面提到的扩展ID是相同的;LoaderName为扩展加载器名称,这后面会详细介绍;VirtualPath为扩展的虚拟路径,可能是目录路径或文件路径——这要看是被哪个扩展加载器加载。
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进行分组:
var availableExtensionsProbes1 = _parallelCacheContext
.RunInParallel(availableExtensions, extension =>
_loaders.Select(loader => loader.Probe(extension)).Where(entry => entry != null).ToArray())
.SelectMany(entries => entries)
.GroupBy(entry => entry.Descriptor.Id);
然后根据探测条目优先级(Priority)、"扩展依赖的虚拟路径的最后修改时间"及扩展加载器的Order属性排序:
var availableExtensionsProbes = _parallelCacheContext
.RunInParallel(availableExtensionsProbes1, g =>
new { Id = g.Key, Entries = SortExtensionProbeEntries(g, virtualPathModficationDates)})
.ToDictionary(g => g.Id, g => g.Entries, StringComparer.OrdinalIgnoreCase);
排序算法在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:10
ReferencedExtensionLoader:20
PrecompiledExtensionLoader:30
DynamicExtensionLoader:100
RawThemeExtensionLoader: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"文件的不包含扩展名的文件名。
var references = _parallelCacheContext
.RunInParallel(availableExtensions, extension => _loaders.SelectMany(loader => loader.ProbeReferences(extension)).ToList())
.SelectMany(entries => entries)
.ToList();
//......
var referencesByName = references
.GroupBy(reference => reference.Descriptor.Id, StringComparer.OrdinalIgnoreCase)
.ToDictionary(g => g.Key, g => g.AsEnumerable(), StringComparer.OrdinalIgnoreCase);
(5)、ReferencesByModule:IDictionary<string, IEnumerable<ExtensionReferenceProbeEntry>>型。按扩展引用探测条目名称(ExtensionReferenceProbeEntry的Name属性)分组的扩展引用探测条目集合。ReferencesByModule与ReferenceByName类似,唯一的不同就是分组方式。
var referencesByModule = references
.GroupBy(entry => entry.Name, StringComparer .OrdinalIgnoreCase)
.ToDictionary(g => g.Key, g => g.AsEnumerable(), StringComparer .OrdinalIgnoreCase);
(6)、VirtualPathModficationDates:ConcurrentDictionary<string, DateTime>型。扩展对应的"扩展依赖虚拟路径最后修改时间"字典。该属性是冗余的。
(7)、AvailableExtensions:List<ExtensionDescriptor>型。当前可用的扩展描述(ExtensionDescriptor型)对象集合。该集合是上面获取的availableExtensions集合通过扩展ID(也就是扩展的目录名称)和扩展之间的依赖关系两个条件排序而来,这类似于Vistual Studio解决方案的按"项目生成顺序"显示的列表。
var sortedAvailableExtensions =
availableExtensions.OrderByDependenciesAndPriorities(
(item, dep) => referencesByModule.ContainsKey(item.Id) &&
referencesByModule[item.Id].Any(r => StringComparer.OrdinalIgnoreCase.Equals(dep.Id, r.Name)),
item => 0)
.ToList();
2、从扩展加载上下文(Extension Loading Context)获取已经被移除的扩展,并通知扩展对应的扩展加载器(Extension Loader)
foreach (var dependency in context.DeletedDependencies) {
Logger.Information( "Extension {0} has been removed from site" , dependency.Name);
foreach (var loader in _loaders) {
if (dependency.LoaderName == loader.Name) {
loader.ExtensionRemoved(context, dependency);
}
}
}
如 上所述,扩展加载上下文的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)
foreach (var extension in context.AvailableExtensions) {
ProcessExtension(context, extension);
}
扩展加载器可能会把扩展需要用到的程序集的复制操作(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、执行程序集复制或删除操作
ProcessContextCommands(context);
根据扩展加载上下文的DeleteActions属性和CopyActions属性,完成程序集的删除或复制操作:
private void ProcessContextCommands(ExtensionLoadingContext ctx) {
Logger.Information( "Executing list of operations needed for loading extensions..." );
foreach (var action in ctx.DeleteActions) {
action();
}
foreach (var action in ctx.CopyActions) {
action();
}
}
5、将扩展依赖信息存储到xml文件中
_dependenciesFolder.StoreDescriptors(context.NewDependencies);
_extensionDependenciesManager.StoreDependencies(context.NewDependencies, desc => GetExtensionHash(context, desc));
将所有扩展的依赖关系写入到~/App_Data/Dependencies/dependencies.xml文件中。
另,还会写入~/App_Data/Dependencies/dependencies_compiled.xml文件中,这里暂不关注。
6、如有必要,重启应用程序域
if (context.RestartAppDomain) {
Logger.Information( "AppDomain restart required." );
_hostEnvironment.RestartAppDomain();
}
如果"重启应用程序域标记"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 : IOrchardHost
Orchard.Environment.Extensions.ExtensionLoadingContext
Orchard.Environment.Extensions.ExtensionLoaderCoordinator : IExtensionLoaderCoordinator
Orchard.Environment.Extensions.ExtensionManager : IExtensionManager
Orchard.Environment.Extensions.Folders.ExtensionHarvester : IExtensionHarvester
Orchard.Environment.Extensions.Folders.ModuleFolders: IExtensionFolders
Orchard.Environment.Extensions.Folders.CoreModuleFolders: IExtensionFolders
Orchard.Environment.Extensions.Folders.ThemeModuleFolders: IExtensionFolders
Orchard.FileSystems.Dependencies.DefaultDependenciesFolder:IDependenciesFolder
Orchard.Environment.Extensions.Models.ExtensionDescriptor
Orchard.Environment.Extensions.Loaders.ExtensionProbeEntry
Orchard.Environment.Extensions.Loaders.ExtensionReferenceProbeEntry
Orchard.FileSystems.Dependencies.DependencyDescriptor
Orchard.FileSystems.Dependencies.DependencyReferenceDescriptor
Orchard.Environment.Extensions.ExtensionEntry
Orchard.Environment.Extensions.ExtensionMonitoringCoordinator : IExtensionMonitoringCoordinator
Orchard.Environment.Configuration.ShellSettings
Orchard.Environment.Configuration.ShellSettingsManager: IShellSettingsManager
Orchard.Environment.ShellBuilders.ShellContext
Orchard.Environment.ShellBuilders.ShellContextFactory: IShellContentFactory
Orchard.Environment.ShellBuilders.ShellContainerFactory
Orchard.Environment.ShellBuilders.CompositionStrategy: ICompositionStrategy
Orchard.Caching.DefaultParallelCacheContext