原文: http://docs.orchardproject.net/Documentation/How-Orchard-works
概述
本文翻譯僅供學習之用,了解Orchard工作原理設計思想、技術點及關鍵詞,如有缺漏請不吝指正。鑒于能力有限定有諸多曲解或不完整的地方,請海涵。不定時完善整理。
CMS不像常規的web程序,它更像一個程序容器。 設計系統時采用一個開放類型的架構,擴展作為首要特性是必需的。
架構圖:
基礎:
主要使用以下現有的框架和類庫
- ASP.NET MVC: web開發框架。 MVC 關注分離
- NHibernate: ORM工具. 將Orchard 內容持久化 、通過移除模塊開發的持久化關注極大的簡化了數據模型
- Autofac: IoC container 依賴注入容器 . 大量使用DI (dependency injection 依賴注入 ) 。實現簡單,而且范圍生命周期都由Orchard Framework進行管理 .( IAuthorizationService, RolesBasedAuthorizationService and XmlRpcHandler.)
- Castle Dynamic Proxy: 動態代理.
Orchard Framework
Orchard Framework是建立在這些基礎上的一個額外的抽象層。也是最深的層。它包含了程序引擎 還有不能被孤立成模塊的部分,他們就是基礎模塊依賴的--也可以成為基礎類庫。
啟動Orchard
web application啟動后,Orchard Host被創建,在應用程序域級別是個單例。
the hos獲得使用ShellContextFactory的當前租戶(current tenant)的Shell。租戶:程序的實例,該程序按照用戶隔離,運行在同一應用程序域中, 提高了站點密度(其實是一個邏輯站點的意思。Orchard支持在一個物理的站點上建立多個邏輯的站點。每個邏輯站點都是獨立,有自己的數據,自己的主題等) 。
Shell是Tenant級別的單例,并且代表了該Tenant。他可以有效提供tenant-level的隔離同時對多租戶保持模塊 編程模型無關的。
shell一旦被創建就可以獲得ExtensionManager可用擴展列表。擴展包括模塊和主題.默認實現是掃描 模塊主題的擴展目錄。
同時從ShellSettingsManager獲得該用戶的設置列表。默認實現從App_Data子文件夾中獲取,但是也可以從其他地方獲得。
shell獲得CompositionStrategy對象,并用它為當前host的擴展列表和當前租戶的設置列表準備IoC容器。它返回的結果ShellBlueprint,不是shell的IoC容器,他是依賴、控制器、記錄藍圖的列表。
ShellSetting列表(每個租戶) 和ShellBluePrint 被扔到ShellContainerFactory.CreateContainer 獲得ILifetimeScope--主要使IoC容器界定范圍在租戶級別以便模塊依賴注入時也被指定到當前租戶不需要任何特別配置。
Dependency Injection 依賴注入
Orchard中,創建可注入的依賴關系的標準方法:創建一個接口繼承IDependency或其派生接口,然后實現該接口。在
消費方,在構造函數采用該接口類型的參數。framework將會發現所有依賴關系、負責實例化并按需注入實例。中
三種不同的依賴關系范圍,通過繼承相應的接口:
?Request: 每次HTTP請求都為創建依賴實例 請求一旦被處理就會將其銷毀。繼承IDependency。 reasonably cheap to create
?Object: 一個對象對接口產生依賴性就會每次創建一個新的實例。實例不會共享。 繼承ITransientDependency。 extremely cheap to create.
?Shell: 每個shell/tenant有且只有一個實例。 繼承ISingletonDependency. 需要維持shell生命周期的狀態 。
替換現有的依賴
通過OrchardSuppressDependency特性修飾類進行現有依賴的替換--使用完全限定的類型名稱作為參數進行替換。
依賴排序
一些依賴不是唯一的 而是列表的一部分。一些依賴不是唯一的 而是列表的一部分。通過修改模塊的清單中 feature 的Priority屬性。
Features:
Orchard.Widgets.PageLayerHinting:
Name: Page Layer Hinting
Description: ...
Dependencies: Orchard.Widgets
Category: Widget
Priority: -1
ASP.NET MVC
Orchard建立在ASP.NET MVC,但為了添加如主題、租戶隔離等功能,引入了一個額外的中間層 用于呈現mvc端 的期望內容 并且在Orchard端 按照Orchard內容級別上分割事情。
請求特定視圖時,LayoutAwareViewEngine開始啟動。 嚴格來說 ,這個不是一個新的視圖引擎,他不關注真正的呈現,它通過查找依賴于當前主題的正確視圖,并將 呈現工作委托真正的視圖引擎。
相似的, route providers, model binders and controller factories 作為 單一入口點而且將請求分派到恰當底層對象。
對于routes,我們有 多個 providers of routes(一般來自 modules 層)和 one route publisher .model binders controller factories 也是如此。
Content Type System 內容類型系統
Orchard中為了提供必要的靈活性,內容通過實際的類型系統進行管理,該系統在某種程度上比.NET基礎類型更加豐富、動態化:類型在運行時動態組成并反射內容管理的關注點.
Types, Parts, and Fields
任意內容類型,包含站點管理員通過code-free方式創建的動態內容.這些內容有 具體處理一個特定的問題分內容聚合而成。原因是很多關注點涉及很多類容類型。
例如:blog列表、產品、一段視頻都會有路由地址、注釋和標簽。因此這些在Orchard中會被按照單獨的內容部分進行處理。 注釋管理模塊按照這種方式可以適用于任意內容類型,包括不明所以然的東東。
Parts 包含properties 、 content fields。Content fields 可以同樣的方式復用。特定的字段類型將會被 系統部分或者內容類型 代表性的使用。
parts fields 的區別:操作的規模 以及語義。
Fields比parts更顆粒度更細。
例如 field 類型描述手機號碼或者坐標,然而 part類型描述的是一個整體(注釋(動作 包含lifetime lifecyle) 標記).
最重要的區別在于語義: part = "is a" 關系 field ="has a" 關系。
例如, T恤是一個產品 它有SKU和價格 屬性。你不會說T恤是個產品 T恤是個價格
T恤是個SKU.
"Shirt" content type 由 Product part組成。Product part由 稱作price 的Money field 和稱作 SKU 的String field組成。
其他的區別 你只有有且一部分 的給定類型/內容類型,讓你認為這是 is-a 關系
因此 part 有很多給定類型的fields.換而言之, part中fields 相當于field's t類型的字典。content type就是part類型的列表(不包含名字)。
如果你想content type有多個實例選擇field類型。
Content類型剖析
content 類型 通過 content parts 類型構建. 代碼級別,Content parts通常和如下相關的:
Record(記錄), part數據的POCO持久化.
model class 繼承并實現ContentPart<T> T record type
repository(數據倉庫) 不需要有模塊實現 負責數據訪問的對象
handlers(處理器). 實現IContentHandler接口 是一組事件處理器 是事件像 OnCreated 或 OnSaved.
基本上在content item的生命周期內使用鉤子執行大量的任務.也參與content items構造函數的構建?;贑ontentHandler的 Filters collection可以讓handler添加對content type添加常見的行為。
例如: StorageFilter 更加方便的聲明 content part的持久化如何處理,只需執行代碼
Filters.Add(StorageFilter.For(myPartRepository)); Orchard系統將會myPartRepository數據持久化到數據庫中。
ActivatingFilter負責 多個 parts 合成一個類型
調用方法Filters.Add(new ActivatingFilter<BodyAspect>(BlogPostDriver.ContentType.Name));
添加body content part到博客帖子drivers(驅動器). 更友好更專業的 handlers處理程序。 繼承ContentPartDriver<T> T a content part type。
另一方面,Handlers是沒有指定類型的content part type。 Drivers 可以看作特定part的controller。 他通過theme引擎呈現形狀。
內容管理
ContentManager對象可以訪問所有的內容。它有對應的方法查詢content store,用于內容版本化和管理發布狀態
事務
每個HTTP請求都會自動創建事務.請求中所有的操作都是環境事務(ambient transaction)的一部分
如果請求的代碼終止了事務,所有的操作將會回滾。事務不能明確的終止,那么所有操作將會提交沒有明確的結果。
Request生命周期
我們以請求一篇博客文章的示例:
一篇博客文章的請求進來后,程序首先查找各個模塊可用的路由 然后查找blog模塊匹配路由。路由為請求指定了博客文章的controller action ,從content manager查找文章。
action基于請求主對像 在content manager調用BuildDisplay獲得Page Object Model (POM)。這樣文章就從content manager取回了。
博客文章有自己的controller,但并不適合所有的content types.核心路由部分的通用ItemController 會處理動態content types。ItemController的Display action 和 blog post controller一樣:通過標頭從content manager獲取內容,然后根據結果構建POM.
依賴布局視圖引擎 確定的正確的視圖根據當前主題和在視圖命名上 model's type連同Orchard conventions 。
視圖內,會生成更多的動態Shape例如區域定義。
通過主題引擎按照出現的順序遞歸生成最終呈現內容,通過尋找正確的模板或者Shape方法來呈現每個在POM的Shape。
Widgets
一種內容類型,包含Widget content part和widget stereotype(鉛版)。同樣由parts 和 fields組成,這樣使用其他內容類型的編輯呈現邏輯進行編輯。共享行為模塊(Building Blocks), 任何現存的content part都會被widget復用,這樣開銷幾乎為0.
通過widget層,添加widget到頁面。該層是widget的集合。它有名字和規則,決定哪些頁面將會展示,widgets列表、相關區域布置、排序、設置。
規則 通過IronRuby表達式 附屬于每層。這些表達式可以使用IRuleProvider的任何實現
Orchard直接提供了兩個:url、authenticated
站點設置
站點是一個內容條目(content item),內容條目可以讓模塊擴展額外的parts。這也是模塊可以配置站點設置的原理。
站點設置租戶級別。
Event Bus 事件總線
Orchard系統和模塊通過創建依賴關系接口、注入實現暴露擴展點。
通過實現的接口或者實現同樣名字和方法的接口借入擴展點。 換句話說,Orchard不需要嚴格的強類型接口通信,使插件以擴展一個擴展點不依賴于程序集的定義。
當擴展點觸發注入實現,事件總線就會發布一條消息。監聽事件總線的對象就會將消息分派給 繼承一個合適的接口的類方法。
Commands 命令
Orchard很多操作可以通過admin UI使用命令行進行。這些命令由實現 CommandName特性修飾的ICommandHandler接口的類方法暴露出來。
命令行工具在運行時通過模擬網站環境和使用反射檢查組件發現可用的命令。該命令運行環境是盡可能接近實際運行的站點。
搜索和索引
搜索和索引的使用Lucene默認情況下實現的, 默認的實現可以被其他引擎所取代。
緩存
Orchard緩存依賴于ASP.NET緩存,但我們通過ICache接口暴露一個輔助的API,使用Get方法調用。如果緩存已經不包含所請求的實體,Get使用key和方法產生緩存實體值。
Orchard緩存API的主要優點是,它每個租戶透明地工作。
文件系統
文件系統根據環境被抽象以便直接存儲在物理文件系統上或者像Azure的云存儲上。體模塊是使用該抽象文件系統模塊的一個例子。
用戶和角色
用戶是內容項(雖然不可路由),例如對于概要文件模塊易于使用其他字段擴展。角色是content part 被焊接到users。
Permissions 權限
每個模塊都可以公開一組權限,以及如何將這些權限默認情況下應授予默認角色。
任務
模塊可以通過調用的實現IScheduledTaskManager接口的CreateTask的類安排任務。通過實現IScheduledTaskHandler執行任務。Process方法檢查任務類型名稱并決定是否處理它。
任務自ASP.NET線程池的一個單獨的線程中運行。
Notifications 通知
模塊可以通過繼承INotifier并調用其方法之一 將管理界面展示消息。 獲取上INotifier的依賴,并調用其方法之一面消息到管理界面。多個通知可以作為任何請求的一部分進行創建。
Localization 本地化
應用程序及其模塊的本地化通過將字符資源包裝后傳入T方法:@T(“此字符串可以本地化”)。 Using the localization helpers
資源管理器從位于程序中的特定位置的PO文件加載本地資源字符串。
內容項目本地化通過不同的機制來實現:內容項目的本地化版本是物理上是由一個特殊的部分鏈接在一起的各個內容項。
culture管理器決定當前使用culture。默認實現返回已在站點設置中配置的culture,還可以從用戶配置文件或從瀏覽器的設置得到它。
Logging 日志
通過繼承ILogger的實現日志。不同的實現可將日志條目發送到不同的存儲類型。 使用 Castle.Core.Logging進行記錄的實現。
Orchard Core
Orchard.Core程序集包含了一組運行必需的模塊。其他模塊可以安全引用這些模塊。
core modules:feeds, navigation 、routable.
Modules 模塊
系統內置模塊如 blogging or pages, 但第三方模塊也容易創建。
模塊僅僅是使用了擴展的manifest.txt文件ASP.NET MVC area。
一個模塊通常包含事件處理程序,內容類型,其默認呈現模板以及一些管理界面。
每當csproj文件或csproj文件引用的文件有變化時, 模塊可以動態??編譯。不需要開發人員甚至使用IDE進行編譯。
模塊必須放置在Modules文件夾(Orchard.Web/Modules/MyModule)和文件夾名稱必須由項目產生的編譯的DLL的名稱相匹配。所以,如果你有一個名為My.Custom.Module.csproj自定義模塊項目,并將其編譯為My.Custom.Module.dll,模塊根文件夾必須命名為My.Custom.Module。 [~/Modules/My.Custom.Module/]
Themes 主題
基本的設計原則,即所有它生成的HTML可以從主題更換,包括模塊產生的標記。Conventions 約定確定文件對應主題的文件層級。
整個渲染機制是基于shapes。該主題引擎的工作是找到當前主題,因為主題確定呈現每個shape的最好方法。每個形狀可以具有可由模塊(views文件夾模板或在代碼shape方法) 定義一個默認的呈現。
該默認渲染可以由當前主題重寫。通過shape的模板版本或shape方法實現。
主題可以有父級,可以讓子主題定制化 或在父級上進行修改。系統提供了名叫Theme Machine的基礎主題作為父主題十分容易使用。
和模塊一樣,主題可以包含很多代碼: 有自己的csproj文件,并從動態編譯受益。主題定義方法,將設置暴露管理用戶界面。
通過繼承IThemeSelector 實現當前主題選擇,它們返回一個主題名稱和任何請求的優先級進行。這讓很多選擇器作出貢獻的主題的選擇。系統提供了IThemeSelector四種實現方式:
- SiteThemeSelector 選擇當前配置的租戶或網站具有低優先級的主題。
- AdminThemeSelector 只要當前URL是一個admin URL,接管并有返回高優先級返回admin theme。
- PreviewThemeSelector 如果當前用戶是發起主題預覽,使用預覽主題重寫站點當前主題
- SafeModeThemeSelector 應用程序是在“安全模式”該選擇器才可使用是唯一的選擇時可用的,通常在安裝過程中發生。它具有非常低的優先級。
主題選擇器的一個例子可能是一個促進當UA被識別為屬于一個移動設備的移動的主題。
引用
參考:http://www.cnblogs.com/esshs/archive/2011/06/01/2067501.html
POCO參考:http://kb.cnblogs.com/page/89750/
環境事物 參考:http://www.cnblogs.com/artech/archive/2010/01/30/1660088.html
POM 參考:http://www.guru99.com/page-object-model-pom-page-factory-in-selenium-ultimate-guide.html
2016.4.17 v0.1