orchard是如何工作的

构建一个web CMS不像构建一个web程序,它更像建一个程序容器。当设计这样的系统时,有必要构建可扩展性作为一流的功能。这可能是一个挑战,因为非常开放的体系结构,它是必要的强大的扩展性类型可能会降低应用程序的可用性。系统中的一切需要与未知的未来模块组合,包括在UI层。

Architecture

Modules
Core
Orchard Framework
ASP.NET MVCNHibernateAutofacCastle
.NETASP.NET
IIS or Windows Azure

 


Orchard Foundations

Orchard CMS基于框架和库构建。下面列出一些重要的:

ASP.NET MVC: ASP.NET MVC 是一个现代的 Web 开发框架,鼓励关注分离。

NHibernate: Nhibernate是一个ORM工具。它处理content items到数据库的持久性,并从模块开发中移除对持久性的关注,这大大简化了数据模型。你可以从任何核心内容类型的源代码中看到这种例子,例如 pages。

Autofac: Autofac是一个IoC Container.Orchard大量使用了依赖注入。创建一个可注入Orchard依赖就是写一个类一样简单,直接或间接实现IDependency 接口,使用依赖很简单,就象采取正确类型的构造器参数。注入的依赖的范围和生存时间由Orchard framework管理。你能在IAuthorizationService的源代码中看到这种示例。

Castle Dynamic Proxy: 使用Castle动态产生代理。

Orchard程序和框架在这些框架的基础上构建额外的抽象层,他们有许多方式实现细节并不需要了解Nhibernate,Castle,Autofac,应要求用Orchard工作。


Orchard Framework

是Orchard的最底层,它包含了应用程序引擎或至少被分离成模块。 这些都是典型的东西,甚至最基本的模块的不得不依赖它,可以将它理解为Orchard的类库。


Booting Up Orchard

当Orchard 启动起来,一个Orchard Host被创建,Host是在应用程序域层的单例。然后,Host将使用ShellContextFactory取得当前租户的Shell,租户是按识别用户被隔离的应用程序实例,他们运行在同一个应用程序域中,这增加了网站密集度。Shell是租户层的单例,事实上代表着租户,它有效提供了租户层隔离对象同时对多租户模块编程模型无关。

Shell创建,就将从ExtensionManager获取可用的扩展。扩展是模块和主题,默认实现了扫描modules和themes目录的扩展。

同时,shell从ShellSettingsManager获取租户的设置列表,默认会从appdata适当子目录获取设置列表,但能从不同地方获取设置。例如:我们有使用blob storage的Azure实现,因为在那个环境中Appdata不一定可写。然后,shell获取CompositionStrategy对象,并从可以扩展列表为当前host准备IoC container,

当前租户的准备设置,这样做的结果不是一个shell的IoC container,它是依赖列表、controller、record blueprints的ShellBlueprint。

每个租户的ShellSettings列表和ShellBluePrint被扔进ShellContainerFactory,CreateContainer得到一个ILifetimeScope,它基本上租户层启用的IoC container范围,因此模块可以在租户范围内得到注入依赖,不用做其它任何具体的事。


Dependency Injection

Orchard中创建可注入依赖的标准方法是直接或间接实现IDependency 接口。在构造器中能得得接口类型的参数。application framework会发现所有依赖并在需要时初始化和注入实例。

有三种不同的可能范围的依赖,选择一个从右边的接口派生的实现。

Request: 为每个http请求创建的依赖实例,并在请求完成后销毁。

Object: 创建新的实例,在接口上对象每一次依赖。实例不共享,派生自ITransientDependency,对象必须很容易创建。

Shell: 每个shell/tenant仅创建一个实例,派生自ISingletonDependency,为shell的生命周期必须维护一个公共策略。


Replacing Existing Dependencies

可能用OrchardSuppressDependency attribute美化的类替换存在的依赖,需要完全合格的类型作为参数去替换。


Ordering Dependencies

一些依赖是不是唯一的,而是有部分列表。例如:处理程序都在同时激活,有些情况你要修改这些依赖创建的顺序,这个可以通过修改模块的manifest实现,使用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构建,为了添加像主题和租户隔离这样的东西,需要引入一个额外的间接层,展示ASP.NET MVC上提出的概念,将在Orchard概念层中分离。

例如:当请求一个指定的view时,LayoutAwareViewEngine引入 ,严格地说,它不是一个新的视图引擎,它包含了依赖当前主题去寻找正确view的逻辑,代理了实际视图引擎的渲染工作。

同样,提供了route providers, model binders and controller factories,作为ASP.NET MVC的单一入口点和在适当范围内对象下分发请求。

在路由情况下,我们能提供很多路由,并且一个路由发布到ASP.NET mvc,model binders and controller factories也是同样的。


Content Type System

Orchard中的内容是在类型系统下管理的,在某些方面是比.net类型系统更丰富和更灵活的一个实际的类型系统。以便给web CMS提供必要的灵活性,类型必须在运行时动态组合以反映内容管理的关注。


Types, Parts, and Fields

Orchard能处理任意内容类型,包括一些管理员以代码的方式动态创建的。这些内容类型是content parts的聚合,因为part关系到许多的内容类型。

例如:博客,产品,视频可能都有路由地址、评论、标签,所以Orchard中路由地址、评论、标签分隔成content part,这样,路由地址、评论、标签模块就只需要开发一次并就用到这些内容类型中。

Parts拥有properties和content fields,fields同样也是可重用的。part和field的区别是他们的范围和语义不同。field比part的粒度更小,例如:field可能描述一个phone number 或 email address,但是part可能就是描述了评论或标签的整个范围。主要的区别还是语义上的:part是实现了”is a”关系,field实现了”has a”关系。例如:一件衬衫is a 产品,has a 名称和价格。


Anatomy of a Content Type

content type是从content parts构建的,content parts 代码通常与下面的有关联:

a Record,是part数据的POCO表示

a Model class,是实际的part,从ContentPart<T>继承,T是record type

a Repository,模块作者不需要实现Repository,Orchard将能使用一个通用的。

handlers, handlers实现了IContentHandler接口,是事件处理的集合,如OnCreated , OnSaved。基本上他们在内容项目的生命周期中执行多项任务。他们也能参与构造器内容项目的实际组成。有一个基于ContentHandler 的Filters集合,为内容类型添加公共行为的处理程序。例如:Orchard提供了一个StorageFilter 使它很容易的声明 如何持久化内容类型 应该怎么处理:just do

drivers. driver是更友好更特别的处理程序(因此不太灵活),并与一个指定的content part type关联(从ContentItemDriver 继承,T 是一个content part type)。另一方面的处理器没有指定一个content part type.Drivers 能看做具体part的 controller,他们通常建立主题引擎呈现的shapes。


Content Manager

所有的内容都通过contentmanager对象访问,这让你使用事先不知道类型的内容成为可能。ContentManager 一些方法,查询内容存储库,内容版本,并管理发布状态。


Transactions

Orchard自动为每个HTTP请求创建一个事务,这意味着请求过程中的所有操作都是事务中的一部分。如果请求代码中断,所有数据操作都将回滚。如果事务没有显示取消,请求结束所有操作都会提交,不需要显示commit。


Request Lifecycle

下面介绍的是对一篇博文的请求。

当请求一篇指定的博文,程序首先寻找各种模块定义的可用的路由并找到博文模块匹配的路由,然后路由解析请求发给blog post控制器的action item,action将从content manager查询博文,这个action从基于请求的主要对象的content manager(叫BuildDisplay)获取 Page Object Model,从content manager检索这个博文。

博文有自己的controller,但不是所有content type都有。例如:动态内容类型被Core Routable part的许多通用ItemController服务,ItemController的显示行为几乎与博文的controller做同样的事,它从content manager取得content item 然后从结果创建POM。

布局视图依赖当前的主题和使用的模块类型一起解析正确的视图,视图内,许多动态的shape被创建,比如zone,找到正确的template或shape method 呈现每个shape ,主题引擎实际呈现在POM中遇到的shape出现的顺序和递归。


Widgets

Widget是有Widget content part和widget stereotype的内容类型。就你其它内容类型,由parts和fields组合而成。这意味着,他们可以使用同一版本的编辑和渲染其他类型的内容的逻辑,他们像积木一样,这意味着任何content part能像widget的一部分被重用。

Widgets 能过widget layers添加到页面,Layers是widget的集合,他们有名称,规则,确定能出现在网站的哪个页面、widget列表、关联zone placement、排序、设置。

每个layer的规则使用IronRuby表达式,在程序中所有IRuleProvider的实现都能使用这些表达式,Orchard有两个box实现:url 和 authenticated.


Site Settings

Orchard中的site是一个content item,使它能为模块添加额外的part,这是模块能如何进行site settings。每个租户都有Site settings。


Event Bus

Orchard和它的模块通过创建依赖接口暴露扩展点,然后实现注入。

通过实现它的接口完成插入扩展点,或实现与接口具有相同的名称和方法。换句话说,Orchard不需要严格的强类型接口通信,启用插件扩展扩展点不需在程序集上定义的地方依赖。

这仅仅是Orchard 事件总线的一种实现,当扩展点用调用注入实现,一条消息被发布在事件总线上,以适当命名的接口派生的类中的方法的对象之一监听事件总线发送的消息。


Commands

Orchard网站的很多行为都能通过命令行执行。这些命令是实现了ICommandHandler的类中的标记了CommandName 特性的方法。Orchard命令行工具在运行时模拟网站环境和使用反射检查程序集来发现可用的命令。命令执行的环境尽可能的实际运行的网站。


Search and Indexing

搜索和索引默认通过使用Lucene来实现,但是默认实现可以用其它索引引擎替换。


Caching

Orchard中的cache依赖 ASP.NET cache,但是我们暴露了一个helper api,可以通过一个ICache类型的依赖调用get方法使用。如果cache没有包含请求项,Get方法取得key和function可用于生成cache entry’s value。使用Orchard cache api的主要好处是每个租户透明地工作。


File Systems

Orchard中的文件系统是抽象的,以便存储能被定向到物理文件系统或Azure的blog存储的备用存储,取决于环境。Media模块就是使用抽象文件系统的例子。


Users and Roles

Orchard中的用户是content items,一个很容易为一个模块的配置并用额外的field扩展它们的content item,角色是能被链接到用户的content part。


Permissions

每个模块都能公开一组权限,以及如何将这些权限授予Orchard的默认角色。


Tasks

模块能通过调用IScheduledTaskManager类型的依赖的CreateTask方法安排任务。该任务能被实现了IScheduledTaskHandler接口的类型执行。这个执行方法能检查任务类型名称并决定是否处理。任务在ASP.NET线程池的一个单独线程上运行。


Notifications

模块能使用INotifier 依赖并调用它的方法在管理界面浮出消息,任何请求的一部分可以创建多个通知。


Localization

程序和模块的本地化通过调用T()方法中包装字符串资源来完成,<%: T("This string can be localized") %>.Orchard的资源管理能从位于程序指定位置的PO文件装载本地化的资源字符串。

Content item的本地化通过不同的机制完成:content item的本地化版本是物理上独立的内容。

目前使用的文化是由culture manager决定。返回文化的默认实现在site setting中设置,但一个备用实现可以从用户配置信息和浏览器的设置获取。


Logging

日志依赖ILogger实现,不同的实现,可以发送到不同存储类型的日志条目。


Orchard Core

Orchard Core程序集包含一组Orhcard运行所必须的模块。其它模块可以安全地依赖到这些模块上,这些模块总是可用的。比如 feeds,navigation,routable模块。


Modules

Orchard默认发行版包含了一些内建模块,像 blogging,pages,但第三方模块也同样能创建。

一个模块就是扩展Orchard的包含manifest.txt文件的ASP.NET MVC area。

一个模块通常包含事件处理程序,内容类型,它们的默认呈现模板以及一些管理界面。

每次修改模块能从源代码动态编译。这种”记事本”风格的开发并没有指定需要显示编译或甚至需要使用VS IDE。


Themes

这是Orchard中的基本设计原则,产生的所有HTML都能用主题替换,包含模块产生的标记。公约定义了文件必须在主题中的文件层次。

Orchard中的渲染机制基于shapes,主题引擎的工作是查找当前主题以及确定渲染每个shape最好的方式。每个shape能有一个默认的渲染,可以由模块在代码中定义 像视图目录中的模板或像一个shape方法。默认渲染可能被当前的渲染覆盖。

主题能有一个父主题,能使子主题专门化或改编。Orchard自带了基主题叫 Theme Machine,设计成作为父主题很容易使用。

主题能包含代码,他们能有自己的csproj文件,有利于动态编译。这使主题定义shape方法,但也暴露拥有的所有设置的管理界面。

当前主题的选择通过实现IThemeSelector的类实现,返回主题名称和任何请求的优先级,Orchard自带了IThemeSelector的4个实现。

SiteThemeSelector 为当前租户的配置或低优先级的网站选择一个主题。

AdminThemeSelector 返加一个高优先级的admin主题,只要当前URL是一个管理 url。

PreviewThemeSelector 预览主题。

SafeModeThemeSelector 当应用程序运行在 safe mode时 是唯一的可选项,通常在安装时发生,它有一个非常低的优先级。

主题选择的一个例子可能是用户使用移动设备时推出一个移动主题。