代码改变世界

Orchard是如何工作的?

2013-04-07 08:28  JustRun  阅读(...)  评论(...编辑  收藏

对Orchard的理解还不深刻,翻译可能有不好的地方.
 
 
开发CMS不同于开发其它的web项目,CMS的首要目标就是要支持扩展.
 
Orchard 架构
Modules
Core
Orchard Framework
ASP.NET MVC NHibernate Autofac Castle
.NET ASP.NET
IIS or Windows Azure
 
 
Orchard的地基
Asp.net MVC
NHibernate
Autofac
Castle Dynamic Proxy
 
Orchard Framework
Orchard Framework是最复杂的部分, 包含了Orchard的引擎和一些不能划分到模块的部分. 几乎所有的Module都依赖于它, 你可以将它看成是Orchard类库。
 
启动Orchard
当Orchard启动的时候,会创建一个Host, host是App domain 级别的单例
下一步, host通过ShellContextFactory取得当前tanant的Shell.
tannat是一个程序实例, 不同的tannat有不同的用户,他们分属于不同的tannant而不知道其它tannat的存在。这个的目的是为了增加网站的密度。
shell是tanant级别的单例, 是tanant的具体呈现, 也是具体实现tannant隔离性的.
shell一旦创建, 将从ExtensionManager中获取所有可用扩展, 这些扩展包括Module和Theme. 默认的实现是会自动扫描Module和Theme文件夹.
同时shell会从ShellSettingsManager获取tannat的setting, 默认的实现会从Appdata的子文件夹中获取。但是可以修改实现从其它地方获取, 比如你有Azure 的实现, 使用blob storage而不是Appdata, 因为Appdata在那个环境下不可写
shell然后取CompositionStrategy对象, 使用它构建IoC容器, 包含当前host的扩展和当前tanant的setting. 结果不是shell的IoC容器, 而是ShellBlueprint, 一个包含依赖项, controller和record的蓝图.
 
依赖注入
典型的在Orchard中使用依赖注入的方法是创建一个接口继承IDependency或者它的子接口,然后创建类实现这个接口。
在使用依赖注入的地方, 你可以把这个接口类型作为构造函数参数, 这样程序就能够自动的发现依赖, 创建实例注入
 
有3中不同的生命周期, 所以要选择好从哪个接口继承:
Request生命周期: 每个Request请求的时候,创建一个,这样的依赖必须创建的开销要小。从IDependency接口继承.
Object生命周期: 这要使用到了,就创建新instance, 这个依赖的必须创建的开销更小. 从ITransientDependency接口继承
Shell 生命周期: 一个shell/tanant才一个实例, 从ISingletonDependency继承。注意只能要来保存一些shell/tanant级别的类型
 
替换已有的依赖
可以通过使用OrchardSuppressDependency  attribute包装你的类来达到替换现有依赖的目的。
 
依赖排序
有些依赖不是一个对象,有可能是一个List.
有时候,你想修改它们的顺序, 这个可以通过修改module的manifest实现, 使用Priority属性, 如下例:
Features:
    Orchard.Widgets.PageLayerHinting:
        Name: Page Layer Hinting
        Description: ...
        Dependencies: Orchard.Widgets
        Category: Widget
        Priority: -1
 
 
Asp.net MVC
Orchard是asp.net mvc实现的,但是为了实现Theme和tanant, 我们添加了一层.
 
比如, 当需要呈现某个view的时候,LayoutAwareViewEngine会先介入. 严格的说,这不是一个新的View Engine, 它不关注如何具体呈现,而是关注如何根据当前的Theme帮助寻找正确的view, 然后通知View Engine渲染.
 
这个类似于route providers, model binders和controller factories, 他们都是实现根据具体请求来实现分发的功能。
 
Content type系统
Orchard中的Content是由真实的类型系统管理, 比.net中的类型系统更加丰富和动态。为了在CMS中提供灵活性,Orchard中的content type必须在运行时组合。
 
Types, Parts和Fields
Orchard能够处理任意的content type, 即使是由网站管理员在coe-free manner中动态创建的. 这些Content type都是为了应付某个特定需求的.
 
比如, 一篇blog,产品和视频都有address, comment和tag. 所以, address, comment和tags在Orchard中被区分为不同的content type. 这样,comment管理模块就只需开发一次然后应用到任意的conent type.
 
Fields比parts的粒度更小. 比如, Field可能只是用于描述一个电话号码, 而part会用来描述comment, tag这样一个完整的部分.重要的区别是, part表示的是"is a"的概念,而field表示的是"has a"的概念
比如, 衣服是产品, 它有SKU和price, 所以衣服这个Content type是由产品part组成, 而产品part是由Money类型的price和sting类型的SKU组成.
另外一个说法是part是一堆fields的字典,每个field有对应的name. content type是有一堆part type组成的。
 
这里给出了如何判断选择part还是field, 如果你的content type是会有多个实例, 那么就应该选择field
 
剖析Content Type
Content type是由Content parts组成的. Content Parts一般有下面组成:
  • a record, 以POCO的方式来呈现part的数据
  • 一个model类继承自ContentPart<T>, 这里的T就是Record的数据类型
  • a repository. 这个repository不是必须有module来提供, 因为Orchard会默认会使用通用的
  • handlers. handlers继承自IContentHandler, 包含一系列的event handlers, 比如OnCreated和OnSaved. 它们是content生命周期的hook, 达到在content生命周期中完成一些任务的目的。它们也可以通过content的构造函数来参与构建content. 在Base ContentHandler中, 有一个Filters的集合, 通过这个集合handlers可以为content type添加一些行为. 比如, Orchard提供了StorageFilter, 使得一个content type非常容易的声明如何实现持久化, 只需要Filters.Add(StorageFilter.For(myPartRepository))
  • drivers. Drivers是更加友好和特殊的handler. handlers不和特定的content type关联, 而drivers可以看做是特定part的一组Controllers. 它们常用于构建shapes, 然后由theme engine渲染.
Content Manager
Orchard中的所有cnotent都是通过ContentManager对象访问的, 这也使得我们能够在不知道content的类型的时候,就能够使用它.
ContentManager提供了方法访问content store, 版本管理contents, 和管理content状态
 
Transactions
Orchard会自动地为每个http request创建transaction. 这也意味着一个request过程中所有的操作都是在transaction管理下的. 如果request abort transaction, 那么所有的数据操作都会回滚. 如果transaction没有被取消,那么在request结束的时候,所有的数据操作会自动提交。
 
Request生命周期
这里,我们拿request获取一篇博客作为例子.
当接收到一个请求要获取一个blog post, 程序首先在所有可用的routes中选择匹配的route, 这些route是有各个modules提供的. 当找到匹配的blog module的route, route会解析到特定的action, action会从content manager中获取post, 然后action从content manager中获取一个Page对象(通过BuildDisplay方法)
 
blog post有它自己的controller, 但是并不是所有的content type都是这样. 比如, 动态的content type是有Core Routable part中对的通用的ItemController来处理的.
 
Layout view enginge将会根据当前的theme和model的类型来使用正确的view. 通过view, 动态shape创建.
 
Widgets
Widgets是content type, 包含widget content part和widget stereotype. 和其它的content types一样, 它也是由parts和fields组成的. 这意味着它们和其它的content type一样编辑和渲染, 也意味着content part也能够比widget使用.
 
widgets通过widget layers添加到pages中. Layers是widgets的组成. 它们有name, 有rule决定网站中的那些pages能够显示, 和一组widgets,相关的zone placement和ordering, settings.
 
layers的rules是以IronRuby expressions的形式表示. 这些expressions能够使用任何从IRuleProvider继承的类.
 
Site Settings
orchard中的site是一个content item, 这就使得modules可以为它拼接其它的parts. 这也是为什么modules能够改变site settings.
Site settings对于每个tenant是独立的.
 
Event Bus
Orchard和它的modules通过interfaces来暴露扩展点, 实现这些interface可以达到注入的目的.
为扩展点扩展可以通过继承这些interfaces, 或者通过实现一个接口,名字一样,方法也一样. 也就是说, orchard对类型没有强制要求.
 
Commands
Orchard中的很多action可以通过admin UI和命令行执行. 这些命令通过类继承ICommandHandler, decorated with CommandName attribute.
 
Orhcard command line tool在运行时发现可用的命令, 模拟web site的环境.
 
Search and Indexing
search和indexing是通过默认使用Lucene, 你也一个通过替换成其它的indexing engine.
 
Caching
Orchard中的cache是基于asp.net的cache, 通过接口ICache的Get方法. Get提供一个key和一个function用来生成对象,如果没有对应key的对象,就用function创建一个.
使用Orchard API的优势是cache对于每个tenant是独立的.
 
File Systems
Orchard中的File system是抽象的, 所以存储可以放到物理存储中, 或者其它的替代, 比如Azure blob storage.
Media module是使用抽象file sytem的一个例子.
 
Users and Roles
在Orchard中, Users是content items, 所以可以非常方便的扩展其它的fields. Roles是content part 用来拼接到Users.
 
Permissions
每个module能够暴露一组permissions, 这些permissions对于orchard的默认roles如何设置
 
Tasks
模块能够创建tasks通过调用IScheduledTaskManager中的CreateTask方法. Task能够被实现IScheduledTaskHandler的执行. 执行处理的方法会检查task的类型和name来决定是否处理这个task.
 
Tasks在一个独立的,从asp.net thread pool上的一个thread上执行.
 
Notifications
模块能够呈现message到admin UI上, 通过INotifier的实现的方法.
 
Localization
程序的Localization是通过包装string资源, <%: T("This string can be localized") %>.
Orchard的资源管理能够从PO文件中加载本地化资源.
Content Item的localization通过其它的方法:localized的content item是通过一个独立的part来实现的.
当前的culture是通过culture manager来决定的. 默认的是通过site settings配置的, 但是可以被user profile和browser's settings覆盖.
 
Logging
Logging是通过ILogger的实现来将日志存储不同的介质的.
 
Orchard Core
Orchard.Core assembly中包含了Orchard必须的modules. 其它的模块可以依赖于这些一定存在的模块.
core modules比如: feeds, navigation, routeable.
 
Modules
Orchard有一些内建的模块, 比如blogging, pages.
modules是一个asp.net mvc area 和manifest.txt文件, 用来扩展orchard.
module典型的包含handlers, content types和默认模板和admin UI.
modules当csproj文件有变动的时候,就能够被动态编译,
 
 
Themes
Orchard的设计的基本原则是所有的Html都是能够通过替换theme来替换的.
Orchard渲染的基础是Shapes. theme engine的工作是找到当前的theme和如何最好的渲染每个shape. 每个shape有个default渲染, 在module中的模板view文件夹或者代码中的shape方法中. default渲染能够被theme重写.
 
Themes可以有个parent.
Themes能够和modules一样包含代码, 能够有自己的csproj文件, 能够动态编译.
选择当前theme是通过IThemeSelector实现, 会返回一个theme name和针对request的priority. Orchard有4种实现IThemeSelector:
 
  • SiteThemeSelector使用在Site或者tenant中的设置, 优先级低.
  • AdminThemeSelector如果当前是admin URL, 使用admin theme. 优先级高
  • PreviewThemeSelector是如果当前是预览模式下使用预览的theme
  • SafeModeThemeSelector是只有当程序运行在"safe mode"的时候才使用的. 级别低.
theme selector的使用的一个例子是当发现请求是从mobile过来的,就是用mobile theme.