九、其它话题(一) Orchard Dynamic Compilation
Introduction
做为可组装的CMS,Orchard能在运行时装载一组随意的模块.
Orchard像任何ASP.NET MVC程序一样,支持使用VS将模块编译成程序集,Orchard也提供一个定制模块装载策略,例如:允许装载关于模块的程序集(不用部署到bin文件夹).
除此之外,Orchard支持仅部署源代码的模块的动态编译,这比部署二进制文件更具灵活性,使一些有趣的场景,如”in place"不用使用VS进行代码定制,这有点像ASP.NET App_Code目录,除了Orchard独立地支持多”logical folder”(通常每个模块一个).
High Level Overview
当Orchard程序启动,Orchard框架需计算出网站中安装了哪些模块和激活了它们(通常装载它们的程序集).
在高层,这个过程能划分成3个独立阶段:
- Discovery: 计算出网站中出现了哪些模块
- Activation:计算出什么策略用来激活(或装载)每一个模块
- References Resolution: 计算出每个模块需要被激活的程序集引用是哪些,这个阶段是"activation"阶段的技术部分,但它更容易思考作为分离的部分引用 解决方案的问题
Discovery
Orchard安装的可用模块列表是通过搜索一些目录中的module.txt文件建立的,默认搜索下面列出的目录:
"~/Modules" folder
Modules目录的目的是包含大多数Orchard模块,约定是每个模块在以模块名称命名的子目录中存储,并包含一个module.txt文件,打包,发布,分享模块仅支持Modules目录中的模块.
"~/Core" Folder
Core文件夹包含了定义在Orchard.Core程序集中的模块,这些模块是Orchard系统核心的一部分,并不能像Modules目录中的模块一样被随意地修改.
"~/Themes" Folder
Themes包含了Orchard的主题,关于动态编译,主题几乎完全被视为模块,除了主题没有代码(bin中的程序集或.csproj文件),本页面的其余部分,当提到模块,应该明白这个概念适用于主题.
Example
这是Orchard安装的例子,包含了6个模块:Common,Localization,Foo,Bar
RootFolder Core Common module.txt <= "Common" module from "Core" Localization module.txt <= "Localization" module from "Core" Modules Foo module.txt <= "Foo" module Bar module.txt <= "Bar" module Themes T2 theme.txt <= "T1" theme T2 theme.txt <= "T2" theme
Activation
每次Orchard从发现阶段收集所有的Module.txt文件,Orchard使用完全分开的策略(或叫Module loaders)装载这些模块到内存中,本质地,装载一个模块的行为是用一个module.txt文件做为输入返回一个System.Type列表做为输出的活动,注意这是比简单地返回System.Assembly稍微通用的,如它允许Orchard支持每个程序集的多模块。例如:Orchard.Core.dll程序集当前包含了大约10个模块
Orchard 框架目前实现了下面的loaders:
"Referenced Module" Loader
这个loader可以在/bin目录中看到,程序集名称与在module.txt中指定的模块名称一致,如果程序集存在,将被装载并返回的它类型,这个loader当某人想要部署一个Orchard网站时非常有用,所有模块都是预编译的并存储在ASP.NET网站程序的/bin目录中.
"Core Module" Loader
如果module.txt指出模块来自于/Core目录,CoreExtensionLoader从"Orchard.Core"程序集的 "Orchard.Core.<moduleMame>" namespace返回types,"Orchard.Core"是一个特殊的程序集,包含了系统的核心模块,即提供了Orchard框架的基本功能。
"Precompiled Module" Loader
如果module.txt指出模块来自于/Modules目录,PrecompiledExtensionLoader在"~/Modules/<ModuleName>/bin"目录中查找一个名叫"<ModuleName>"的程序集,如果文件存在,它就会被复制到"~/App_Data/Dependencies"目录中,这是一个特殊的目录,用于ASP.NET从传统的"~/bin"之外查找额外的程序集.
"Dynamic Module" Loader
如果module.txt指出模块来自于"~/Modules"目录,"Dynamic Module" loader在"~/Modules/<ModuleName>"查找一个名叫".csproj"的文件,如果这个文件存在,该loader会为".csproj"使用Orchard build manager编译这个文件为程序集并返回程序中的types.
注意:这个loader是唯一一个系统执行中经常被称为 "dynamic compilation"的loader。
Loader Disambiguation
由于有许多loader能装载一个给定的模块,Orchard不得不有一个方式解析歧义,即找到正确的loader,每个loader有它们能装载模块的最后修改时间的能力,对于一个给定的模块,如果有多个候选loaders,Orchard
将会按 最近修改的日期返回的loader。
例如:一个给定的模块能用 包含.csproj文件和编译成程序集放进它的bin目录 两种完整的源码形式发布,第一次模块被装载,Orchard将为bin目录中的程序集选择loader,由于它很可能是在源码最后更改后被编译,不管怎样,如果源码有任何改变,"Dynamic Module" loader都将返回最近修改文件的日期(源文件或csproj)
,Orchard将为给定的模块选择那个loader。
注意:"Core Module" loader从来不是有歧义的,因为仅有一种方式装载这些模块,歧义公能在"~/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
References Resolution
解释Orchard如何通过查看csproj 文件的 "References" 节也查看每个模块的bin目录中的额外二进制程序集 识别出references 。
Change of Configuration Detection
像上面解释的,模块在程序启动时装载,不过,每次程序启动,改变都会发生,可能安装了一个新模块,模块的源码可能是手动更新的,一个模块可能从网站移除了,等等,要检测这些改变,Orchard要求系统中的每个模块loader "monitor" 潜在的变化,并当发生变化时发出通知。
当检测到变化,当前模块配置被丢弃,如果程序再重新启动了,模块会重新验证、装载和激活。在某些情况,这个改变会请求ASP.NET 应用程序域重启(如一个新版本模块程序集需要装载),Orchard检测这些状况并让ASP.NET 应用程序域重启。
Rendering Web Forms Views
TODO:解释当读取.ascx和aspx文件时,Orchard使用一个自定义的虚拟路径提供插入自定义的 "Assembly Src=xx" and "Assembly Name=xxx"指令。
Rendering Razor Views
TODO:解释Orchard使用一个Razor自定义的API为Views添加模块依赖。
The "~/App_Data/Dependencies/Dependencies.xml" File
这个文件包含了模块的列表、它们的loader和它们的 模块的”最后已知良好“的配置的参考,即Orchard最后成功装载程序模块的时间,检查这个文件的内容对于调试是很有用的,即如果一个最新版本的模块好像没有被加载。