请注意,本站并不支持低于IE8的浏览器,为了获得最佳效果,请下载最新的浏览器,推荐下载 Chrome浏览器
欢迎光临。交流群:166852192

Orchard中的内容部件(Content Part)是如何工作的


    在《理解Orchard中的内容管理》一文中我们介绍了Orchard中组织内容的方式。其中谈到了一个非常关键东西——内容部件(Content Part)。每一个内容部件都是一个完整的小功能块,它里面实现自身功能的UI呈现和数据存取,这也就是说它实现了一个从UI层到数据库层的完整封装。那这个功能是如何实现的呢?
    我们知道在ASP.NET MVC中有一个PartialView 的概念,可以实现一个UI层的封装,如果这个部分的数据需要单独存储,那么就是还是需要由相应action来执行。但是Orchard的内容部件可能是要在其他模块中调用,其存储数据操作也是在其他模块中的action中执行的。如何很好的解决在其他模块的action中执行自身内容部件数据存储及显示的问题,是实现内容部件功能的关键。在Orchard中引入了两个东西来解决这个问题——驱动器(Driver)和处理器(Handler)。

驱动器(Driver)

驱动器是一个类,通常继承于Orchard框架中的ContentPartDriver,可重写其查看界面显示方法(Display)、编辑界面显示方法(Editor—Get时执行),编辑界面提交方法(Editor—Post时执行),还有其他一些导入导出之类的方法。从这些重写的方法来看,驱动器就好比是内容部件的Controller,驱动器中的方法就好比是内容部件的action。

处理器(Handler)

处理器也是一个类,通常继承于Orchard框架中的ContentHandler,它定义了相应内容部件的行为、事件以及在呈现前操作数据模型。我们可以把它理解为内容部件的Filter,负责告诉Orchard框架如何处理你的Part。

如何工作

当有一个request请求需要显示一个内容的时候。系统首先是根据Route规则找到该请求对应的Controller中对应的action。然后在此action中通过IContentManager接口取获取内容。在用IContentManager获取内容的时候,Orchard框架会自动通过相关内容部件处理器所提供的数据存储Filter来获取相关部件的数据。接下来通过BuildDisplay构建一个需要显示到视图上的动态类Model,组成内容的各个部件都作为这个动态类的属性。最后Orchard视图引擎根据各部件驱动器所提供的显示方法,找到对应的显示模板,最终组合成一个UI呈现给用户。呈现编辑页面和提交编辑数据的原理大致相同。内容部件工作示意图如下:

上图只是简单的描述了一下获取内容部件数据的执行流程,要想用好这一功能还需要仔细研究代码好好体会(可查看Orchard核心层关于内容显示部分的代码:\Orchard.Web\Core\Contents\Controllers\ItemController.cs)。另外Orchard的视图引擎功能也需要好好研究,这样才能更好的掌握灵活定制界面的功能。在这里姑且先把它理解成一个黑盒子。只要给它一个动态对象,它就能根据构成这个动态对象的部件分别找到呈现的模板,从而组合输出我们想要的界面。等有深入了解后再来揭开这个黑盒子的秘密吧。

示例

如果我们需要实现一个产品内容部件,其功能就是显示产品的一些特有信息,如:价格、品牌等。
其驱动器的代码如下:
 
ProductPartDriver.cs
using System;
using System.Linq;
using JetBrains.Annotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Handlers;
using Orchard.Security;
using MyCompany.Products.Models;

namespace Orchard.Products.Drivers 
{    
    
/// <summary>
    
/// Driver相当于内容部件的Controller
    
/// </summary>
    public class ProductPartDriver : ContentPartDriver<ProductPart> 
    {
        
/// <summary>
        
/// 显示界面显示时执行(相当于Action)
        
/// </summary>
        
/// <param name=part>相当于此Part的Model</param>
        
/// <param name=displayType>显示类型(如:details(详情显示)或 summary(摘要显示))</param>
        
/// <param name=shapeHelper>类似视图引擎之类的东西,可以根据相应显示的动态对象去找对应的显示模板(相当于View)</param>
        
/// <returns>这里相当于是返回了一个ActionResult,Orchard框架会针对这个返回值进行相应处理</returns>
        protected override DriverResult Display(ProductPart part, string displayType, dynamic shapeHelper)
        {
            
return ContentShape(Parts_Product, () => shapeHelper.Parts_Product(
                Price: part.Price,
                Brand: part.Brand));
        }

        
/// <summary>
        
/// 编辑界面显示时执行(Get)
        
/// </summary>
        
/// <param name=part></param>
        
/// <param name=shapeHelper></param>
        
/// <returns></returns>
        protected override DriverResult Editor(ProductPart part, dynamic shapeHelper)
        {
            
return ContentShape(Parts_Product_Edit,
                () 
=> shapeHelper.EditorTemplate(
                    TemplateName: 
Parts/Product,
                    Model: part,
                    Prefix: Prefix));
        }

        
/// <summary>
        
/// 编辑界面提交时执行(Post)
        
/// </summary>
        
/// <param name=part></param>
        
/// <param name=updater></param>
        
/// <param name=shapeHelper></param>
        
/// <returns></returns>
        protected override DriverResult Editor(ProductPart part, IUpdateModel updater, dynamic shapeHelper)
        {
            updater.TryUpdateModel(part, Prefix, 
nullnull);
            
return Editor(part, shapeHelper);
        }
    }

 
其处理器的代码如下:
 
ProductHandler.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.ContentManagement.Handlers;
using MyCompany.Products.Models;
using Orchard.Data;

namespace MyCompany.Products.Handlers
{
    
/// <summary>
    
/// 处理器相当内容部件的Filter
    
/// 目前仅针对该产品内容部件的存储进行定义
    
/// </summary>
    public class ProductHandler : ContentHandler 
    {
        
public ProductHandler(IRepository<ProductRecord> repository)
        {
            
//定义ProductPart的数据存储是通过IRepository<ProductRecord>进行处理的
            
//IRepository就把它理解为数据访问层类
            Filters.Add(StorageFilter.For(repository));
        }
    }

 

总结

通过本文,我们又学习了Orchard中的两个概念:驱动器和处理器,并了解了它们是如何工作、如何实现内容部件功能的。这部分的内容,是Orchard中最具特色的地方,了解和掌握这部分的内容就可以向开发真正具有Orchard特色的模块进发了
 
参考文档:
写一个内容部件:http://www.orchardproject.net/docs/Writing-a-content-part.ashx
理解内容处理器:http://www.orchardproject.net/docs/Understanding-content-handlers.ashx

作者原创内容不容易,如果觉得内容不错,请点击右侧“打赏”,赏俩给作者花花,也算是对作者付出的肯定,也可以鼓励作者原创更多更好内容。
更多详情欢迎到QQ群 166852192 交流。