Introduction
介绍
As a composable CMS, Orchard has the ability to load an arbitrary set of modules (also known as "extensions") at run-time. One of the goals of the 0.5 release was to make the process of installing and updating modules as easy as possible.
作为可组合的CMS,Orchard能够在运行时加载任意模块集(也称为“扩展”)。 0.5版本的目标之一是尽可能简化安装和更新模块的过程。
Orchard, like any ASP.NET MVC application, supports loading modules compiled as assemblies using Visual Studio. Orchard also offers a customized module loading strategy which, for example, allows loading assemblies for modules without having to deploy them in the ~/bin
folder.
与任何ASP.NET MVC应用程序一样,Orchard支持使用Visual Studio加载编译为程序集的模块。 Orchard还提供定制的模块加载策略,例如,允许加载模块的程序集,而不必将它们部署在〜/ bin
文件夹中。
In addition to that, Orchard supports the ability to dynamically compile modules deployed as source code only (this is still somewhat experimental). This is more flexible than deploying binaries, and enables some interesting scenarios such as "in place" code customization without having to use Visual Studio. This is somewhat similar to the ASP.NET App_Code
directory, except Orchard supports multiple "logical folders" (typically one per module) independently.
除此之外,Orchard还支持动态编译仅作为源代码部署的模块的能力(这仍然是一些实验性的)。这比部署二进制文件更灵活,并且可以启用一些有趣的方案,例如“就地”代码自定义,而无需使用Visual Studio。这有点类似于ASP.NETApp_Code
目录,除了Orchard独立支持多个“逻辑文件夹”(通常每个模块一个)。
The goal of this section is to describe at a technical level how Orchard loads modules in the 0.5 release. This feature is often referred to as "Orchard Dynamic Compilation", even though technically dynamic compilation is only involved in very specific cases.
本节的目标是在技术层面描述Orchard如何在0.5版本中加载模块。此功能通常称为“Orchard动态编译”,即使技术上动态编译仅涉及非常具体的情况。
High Level Overview
高级概述
When an Orchard application starts, the Orchard Framework (the ExtensionLoaderCoordinator
class to be precise) needs to figure out what are the modules installed in the Web Site and activate them (typically by loading their assembly).
当Orchard应用程序启动时,Orchard Framework(确切地说是“ExtensionLoaderCoordinator”类)需要弄清楚Web站点中安装的模块是什么并激活它们(通常通过加载它们的程序集)。
At a high level, this process can be divided in 3 distinct phases:
从较高的层面来看,这个过程可分为3个不同的阶段:
-
Discovery: figure out what are the modules present in the web site
-
Discovery:弄清楚网站上的模块是什么 *
-
Activation: figure out what strategy to use to "activate" (or load) each module
-
Activation:找出用于“激活”(或加载)每个模块的策略 *
-
References Resolution: figure out what are the assembly references needed to be activated for each module. This phase is technically part of the "Activation" phase, but it is easier to think about the problem of reference resolution as a separate concern.
-
References Resolution:找出每个模块需要激活的程序集引用。此阶段在技术上是“激活”阶段的一部分,但更容易将参考分辨率问题视为一个单独的问题。 *
Once modules are properly activated, they are further examined to detect and enable individual features, but this is a topic for another section.
一旦模块被正确激活,它们将被进一步检查以检测并启用单个_features_,但这是另一部分的主题。
Discovery
发现
The list of available extensions in an Orchard installation is built by searching various folders of the file system for Module.txt
and Theme.txt
files. The folders looked at by default are listed in the following sections.
通过在“Module.txt”和“Theme.txt”文件中搜索文件系统的各种文件夹来构建Orchard安装中的可用扩展列表。默认情况下查看的文件夹将在以下部分中列出。
~/Modules
Folder
〜/ Modules
文件夹
The ~/Modules
folder is intended to contain the vast majority of Orchard modules. The convention is that each module is stored in a sub-folder named <ModuleName>
containing a single Module.txt
file. Packaging, distribution and sharing of modules is only supported for modules in the ~/Modules
folder.
〜/ Modules
文件夹旨在包含绝大多数Orchard模块。惯例是每个模块都存储在一个名为<ModuleName>
的子文件夹中,该子文件夹包含一个Module.txt
文件。模块的打包,分发和共享仅支持〜/ Modules
文件夹中的模块。
~/Core
Folder
〜/ Core
文件夹
The ~/Core
folder contains, by convention, modules defined in the Orchard.Core
assembly. These modules are part of the "Core" Orchard system and are not intended to be modified as freely as modules in the ~/Modules
folder.
按照惯例,〜/ Core
文件夹包含在Orchard.Core
程序集中定义的模块。这些模块是“Core”Orchard系统的一部分,不能像〜/ Modules
文件夹中那样自由地修改。
~/Themes
Folder
〜/ Themes
文件夹
The ~/Themes
folder is intended to contain Orchard Themes. With respect to dynamic compilation, Themes are treated almost exactly the same as Modules, except that Themes don't have to have code (assembly in bin
or .csproj
file). For the rest of this page, when we refer to "Module", it should be understand that the concept applies to "Theme" the same way.
〜/ Themes
文件夹旨在包含Orchard主题。关于动态编译,主题被视为与模块几乎完全相同,除了主题不必具有代码(在bin
或.csproj
文件中汇编)。对于本页的其余部分,当我们引用“模块”时,应该理解该概念以相同的方式应用于“主题”。
Custom Folders
自定义文件夹
Orchard 1.10 introduced a new feature that allows the loading of extensions from custom-defined folders outside of the ones listed above by adding the ExtensionLocations
service that is utilised by each extension loader (see the Loaders
in the Activation
section below).
Orchard 1.10引入了一项新功能,允许通过添加每个扩展加载器使用的ExtensionLocations
服务,从上面列出的文件夹之外的自定义文件夹中加载扩展(请参阅下面“激活”部分中的Loaders
) )。
Additional extension folders can be configured by defining an AppSetting
(e.g. by adding it to the root web.config
file, which contains appropriate examples) with the key Modules
and/or Themes
with their respective value
being e.g. ~/Modules.Custom
and/or ~/Themes.Custom
.
可以通过定义“AppSetting”(例如,将其添加到rootweb.config
文件,其中包含适当的示例)来配置其他扩展文件夹,其中包含键“Modules”和/或“Themes”及其各自的value
例如〜/ Modules.Custom
和/或〜/ Themes.Custom
。
Example
例
Here is an example of an Orchard installation which contains the following extensions: Common
and Localization
(Core
modules), Orchard.Azure
and Orchard.Caching
(built-in modules), SafeMode
and TheAdmin
(built-in themes), MyModule1
and MyModule2
(custom modules), MyBaseTheme
and MyTheme
(custom themes).
下面是一个Orchard安装示例,其中包含以下扩展:Common
和Localization
(Core
modules),Orchard.Azure
和Orchard.Caching
(内置模块),SafeMode
和TheAdmin
(内置主题),MyModule1
和MyModule2
(自定义模块),MyBaseTheme
和MyTheme
(自定义主题)。
Root (Orchard.Web)
Core
Common
Module.txt <= "Common" module from "Core"
Localization
Module.txt <= "Localization" module from "Core"
Modules
Orchard.Azure
Module.txt <= "Orchard.Azure" module
Orchard.Caching
Module.txt <= "Orchard.Caching" module
Modules.Custom
<font color=#0099ff size=4 face="黑体">Modules.Custom</font>
MyModule1
MyModule1
Module.txt <= "MyModule1" module
MyModule2
MyModule2
Module.txt <= "MyModule2" module
Themes
SafeMode
Theme.txt <= "SafeMode" theme
TheAdmin
Theme.txt <= "TheAdmin" theme
Themes.Custom
<font color=#0099ff size=4 face="黑体">Themes.Custom</font>
MyBaseTheme
Theme.txt <= "MyBaseTheme" theme
MyTheme
Theme.txt <= "MyTheme" theme
Activation
激活
Once Orchard has collected all the Module.txt
files from the discovery phase, Orchard uses distinct strategies (or "Module Loaders") to load these modules in memory. Internally, the act of "loading a module" is an activity that takes a Module.txt
file as input and returns a list of System.Type
as output. Note that this is slightly more generic than simply returning a System.Assembly
, as it allows Orchard to support multiple modules per assembly. For example, the Orchard.Core.dll
assembly currently contains about 10 modules.
一旦Orchard收集了发现阶段的所有Module.txt
文件,Orchard就会使用不同的策略(或“模块加载器”)将这些模块加载到内存中。在内部,“加载模块”的行为是一个活动,它将Module.txt
文件作为输入,并返回一个System.Type
列表作为输出。请注意,这比仅返回System.Assembly
稍微更通用,因为它允许Orchard支持每个程序集的多个模块。例如,Orchard.Core.dll
程序集目前包含大约10个模块。
The Orchard framework currently implements the following loaders:
Orchard框架目前实现以下加载器:
"Referenced Module" Loader
“参考模块”装载机
This loader looks in ~/bin
directory for a assembly name corresponding to the module name specified in Module.txt
. If the assembly exists, it is loaded and all its types are returned. This loader is useful when someone wants to deploy an Orchard web site where all modules are pre-compiled and stored in ~/bin
, in a typical "asp.net web application" way.
这个加载器在〜/ bin
目录中查找与Module.txt
中指定的模块名对应的程序集名。如果程序集存在,则会加载它并返回其所有类型。当有人想要部署一个Orchard网站时,这个加载器非常有用,其中所有模块都是以“〜/ bin”的形式预先编译并存储在典型的“asp.net web应用程序”中。
"Core Module" Loader
“核心模块”装载机
If Module.txt
indicates a module from the ~/Core
folder, the CoreExtensionLoader returns the types from the Orchard.Core.<ModuleName>
namespace of the Orchard.Core
assembly. Orchard.Core
is a special assembly containing modules that are "core" to the system, i.e. offering basic functionality on top of the Orchard Framework.
如果Module.txt
表示来自〜/ Core
文件夹的模块,则CoreExtensionLoader从Orchard.Core
程序集的Orchard.Core。<ModuleName>
命名空间返回类型。 Orchard.Core
是一个特殊的程序集,包含对系统“核心”的模块,即在Orchard框架之上提供基本功能。
"Precompiled Module" Loader
“预编译模块”加载器
If Module.txt
indicates a module from the ~/Modules
folder, the PrecompiledExtensionLoader
looks for an assembly named <ModuleName>
in the ~/Modules/<ModuleName>/bin
folder. If the file exists, its is copied to the ~/App_Data/Dependencies
folder, which is a special folder used by ASP.NET to look for additional assemblies outside of the traditional ~/bin
folder.
如果Module.txt
表示来自〜/ Modules
文件夹的模块,PrecompiledExtensionLoader
在〜/ Modules / <ModuleName> / bin
文件夹中查找名为<ModuleName>
的程序集。如果该文件存在,则将其复制到〜/ App_Data / Dependencies
文件夹,该文件夹是ASP.NET用于查找传统〜/ bin
文件夹之外的其他程序集的特殊文件夹。
"Dynamic Module" Loader
“动态模块”装载程序
If Module.txt
indicates a module from the ~/Modules
folder, the "Dynamic Module" loader looks for a file named <ModuleName>.csproj
in the ~/Modules/<ModuleName>
folder. If the file exists, the loader will use the Orchard build manager for .csproj
files to compile the file into an assembly and return all the types from that assembly.
如果Module.txt
表示来自〜/ Modules
文件夹的模块,则“Dynamic Module”加载器在〜/ Modules / <ModuleName>
文件夹中查找名为<ModuleName> .csproj
的文件。如果该文件存在,则加载器将使用Orchard构建管理器来处理.csproj
文件,以将该文件编译为程序集并返回该程序集中的所有类型。
Note: This loader is the only one in the system performing what is often referred to as dynamic compilation
, and is indeed optional if modules have been pre-compiled.
注意:这个加载器是系统中唯一一个执行通常称为“动态编译”的加载器,如果模块已经预编译,它确实是可选的。
Loader Disambiguation
装载机消歧
Since there is potentially more than one loader able to load a given module, Orchard has to have a way to resolve the ambiguity, i.e. pick the "right" loader. Each loader has the ability to return a "date of last modification" for each module they can load. For a given module, if there are multiple candidate loaders, Orchard will pick the loader which returns the most "recent" date of last modification.
由于可能有多个加载器能够加载给定模块,因此Orchard必须有办法解决模糊性,即选择“正确”加载器。每个加载器都能够为它们可以加载的每个模块返回“上次修改日期”。对于给定的模块,如果有多个候选加载器,Orchard将选择返回最后一次修改的“最近”日期的加载器。
For example, a given module can be distributed with both full source code (including .csproj
file) and compiled into an assembly in its bin
directory. The first time the module is loaded, Orchard will pick the loader for the assembly in bin
since it's very likely the assembly was compiled after the last source code change was made. However, if any change was made to the source code afterward, the "Dynamic Module" loader will return the date of the most recently modified file (either the source file or .csproj
), and Orchard will pick that loader for the given module.
例如,给定的模块可以与完整的源代码(包括.csproj
文件)和一起分发,编译到其bin
目录中的程序集中。第一次加载模块时,Orchard将在bin
中选择装配的加载程序,因为很可能在最后一次源代码更改后编译了程序集。但是,如果之后对源代码进行了任何更改,“动态模块”加载器将返回最近修改过的文件的日期(源文件或.csproj
),Orchard将为给定的加载器选择该加载器模块。
Note that the "Core Module" loader is never ambiguous, because there is only one way to load these modules. The ambiguity can only arise for modules in the ~/Modules
directory.
请注意,“核心模块”加载器永远不会模糊,因为只有一种方法可以加载这些模块。模糊性只能出现在〜/ Modules
目录中的模块中。
Example
例
RootFolder
Bin
Orchard.Web.dll
Orchard.Core.dll
Foo.dll
Core
Common <= "Core Module" loader
Module.txt
Localization <= "Core Module" loader
Module.txt
Modules
Foo <= "Reference Module" loader (because a "~/bin/Foo.dll" file exists)
Module.txt
Bar <= "Precompiled Module" loader (because a "~/Modules/Bar/bin/Bar.dll" file exists)
bin
Bar.dll
Module.txt
Baz <= "Dynamic Module" loader (because a "~/Modules/Baz/Baz.csproj" file exists)
Controller
BazControler.cs
Baz.csproj
Module.txt
Disabling the "Dynamic Module" loader
禁用“动态模块”加载程序
The dynamic module loader should be useless when deploying a website in production, as a production enviroment it
在生产中部署网站时,动态模块加载器应该是无用的,作为生产环境
should not be able to install and load module dynamically. But another important reason why it should be disabled
不应该能够动态安装和加载模块。但它应该被禁用的另一个重要原因
is that it creates a lot of FileSystemWatcher
instances to detect changes on the modules.
是它创建了很多FileSystemWatcher
实例来检测模块的变化。
To disable the module, rename the file \Config\Sample.HostComponents.config
to \Config\HostComponents.config
,
要禁用该模块,请将文件\\ Config \\ Sample.HostComponents.config
重命名为\\ Config \\ HostComponents.config
,
then check the content is:
然后查看内容是:
<?xml version="1.0" encoding="utf-8" ?>
<HostComponents>
<Components>
<Component Type="Orchard.Environment.Extensions.ExtensionMonitoringCoordinator">
<Properties>
<Property Name="Disabled" Value="true"/>
</Properties>
</Component>
</Components>
</HostComponents>
Deploy this file and restart the App Pool.
部署此文件并重新启动应用程序池。
NB: You will have to ensure that the binaries for every modules are available in the /bin
folder of each module,
注意:您必须确保每个模块的二进制文件在每个模块的/ bin
文件夹中可用,
such that the Precompiled Module loader can use them directly. When using Visual Studio this should be the case.
这样预编译模块加载器可以直接使用它们。使用Visual Studio时应该是这种情况。
Otherwise use the command line tool to build the website, which will have the same effect.
否则使用命令行工具来构建网站,这将具有相同的效果。
References Resolution
参考决议
(TODO: Explain how Orchard figures out references by looking at the "References" section of the csproj file as well as looking at additional assembly binaries dropped in each module bin
directory)
(TODO:解释Orchard如何通过查看csproj文件的“References”部分以及查看每个模块bin
目录中删除的其他程序集二进制文件来确定引用)
Change of Configuration Detection
配置检测的变更
As explained above, modules are loaded at application startup. However, once the application is started up, changes can happen: a new module might be installed, the source code of a module might be manually updated, a module might be removed from the site, etc. To detect these changes, Orchard asks each module loader in the system to "monitor" potential changes, and notify when a change happens.
如上所述,在应用程序启动时加载模块。但是,一旦应用程序启动,可能会发生更改:可能会安装新模块,可能会手动更新模块的源代码,可能会从站点中删除模块等。要检测这些更改,Orchard会询问每个模块系统中的模块加载器“监视”潜在的变化,并在发生变化时通知。
When a change is detected, the current module configuration is discarded and modules are re-examined, loaded and activated as if the application was starting up again. In some cases, these changes require an ASP.NET AppDomain restart (e.g. a new version of a module assembly needs to be loaded). Orchard detects these situations and forces an ASP.NET AppDomain restart.
检测到更改时,将丢弃当前模块配置,并重新检查,加载和激活模块,就像应用程序再次启动一样。在某些情况下,这些更改需要重新启动ASP.NET AppDomain(例如,需要加载新版本的模块程序集)。 Orchard检测到这些情况并强制重新启动ASP.NET AppDomain。
Rendering Web Forms Views
呈现Web窗体视图
(TODO: Explain that Orchard uses a custom virtual path provider to insert custom Assembly Src=xx
and Assembly Name=xxx
directive when reading .ascx
and .aspx
files)
(TODO:解释Orchard使用自定义虚拟路径提供程序在读取.ascx
和.aspx
文件时插入自定义Assembly Src = xx
和Assembly Name = xxx
指令)
Rendering Razor Views
渲染剃刀视图
(TODO: Explain that Orchard uses a Razor custom API to add Module dependencies to Views)
(TODO:解释Orchard使用Razor自定义API向视图添加模块依赖项)
The ~/App_Data/Dependencies/Dependencies.xml
file
〜/ App_Data / Dependencies / Dependencies.xml
文件
This file contains the list of modules, their loader and their resolved references of the "last known good" configuration of module, i.e. the last time Orchard successfully loaded all modules of the application. Examining the content of this file can be useful for debugging purposes, e.g. if the latest version of a module doesn't seem to be loaded.
该文件包含模块列表,它们的加载器以及它们对模块的“最后已知良好”配置的已解析引用,即Orchard上次成功加载应用程序的所有模块。检查该文件的内容对于调试目的是有用的,例如,如果似乎没有加载模块的最新版本。