概述 本文翻譯僅供學習之用,瞭解Orchard工作原理設計思想、技術點及關鍵詞,如有缺漏請不吝指正。鑒於能力有限定有諸多曲解或不完整的地方,請海涵。不定時完善整理。 CMS不像常規的web程式,它更像一個程式容器。 設計系統時採用一個開放類型的架構,擴展作為首要特性是必需的。 架構圖: 基礎: 主要 ...
概述
本文翻譯僅供學習之用,瞭解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構造函數的構建。基於ContentHandler的 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