Orchard Core 模块开发模式速查:从 Content Part 到后台任务的完整实现范式
发布时间:2026/9/29 21:42:47来源:尧图网络
CMS后端Web框架【免费下载链接】OrchardCoreOrchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.项目地址https://gitcode.com/gh_mirrors/or/OrchardCore点击查看免费下载本指南以 Orchard Core 官方模块开发技能文档patterns.md为核心骨架系统梳理开发一个自包含 Orchard Core 模块所需的十大编码模式Content Part、Display Driver、Content Field、YesSql 迁移与索引、权限提供者、Admin 菜单、内容处理器与后台任务并最终汇总为完整的ConfigureServices注册清单。读完本文你将掌握在 Orchard Core 中搭建一个可展示、可编辑、可查询、可授权、可调度的标准业务模块所需的全部代码范式并能对照仓库源码验证每个模式的底层实现。一、模式总览一个标准模块由哪些构件组成在 Orchard Core 中一个业务模块通常由以下构件组合而成它们分别回答数据长什么样、怎么存取、怎么展示、谁有权访问、入口在哪、何时触发六类问题构件职责文档对应模式Content Part / Content Field定义可附着在内容类型上的数据结构Content Part、Content FieldDisplay Driver负责 Part/Field 的前端展示、编辑表单与提交更新Content Part DriverMigrations Index建表、建索引、版本升级支撑查询性能Migrations (YesSql)、Index DefinitionPermission Provider声明模块自定义权限并绑定默认角色Permission ProviderAdmin Menu在后台管理导航中注入模块入口Admin MenuContent Handler订阅内容生命周期事件创建、发布、删除等Content HandlerBackground Task按 Cron 调度执行的后台作业Background TaskConfigureServices将上述构件逐一注册进依赖注入容器Service Registration in Startup.cs下文按先数据、再展示、后系统集成的顺序逐一展开每个模式均保留原文档完整代码并补充源码级说明与可验证的仓库路径。二、Content Part可复用的内容单元Content Part 是可以附加到任意内容类型上的可复用数据块。它直接继承自OrchardCore.ContentManagement命名空间下的ContentPart基类本质上是一个强类型 POCO属性会被序列化进内容项文档中。using OrchardCore.ContentManagement; namespace OrchardCore.YourModule.Models; public class YourPart : ContentPart { public string Title { get; set; } public string Description { get; set; } public bool IsEnabled { get; set; } }使用要点Part 的属性类型不限可以是标量、复杂对象或集合序列化由内容项的 JSON 存储负责一个 Part 定义一次后可被任意多个内容类型重复挂载实现写一次、处处复用Part 的类型本身不含业务逻辑展示/编辑逻辑全部下沉到对应的 Display Driver 中这是 Orchard Core 的典型分层约定。从源码结构看ContentPart基类与AddContentPartT注册扩展位于 src/OrchardCore/OrchardCore.ContentManagement.Abstractions 下ServiceCollectionExtensions.cs中的AddContentPartTContentPart()会把 Part 类型登记进ContentOptions并返回一个 ContentPartOptionBuilder供后续链式追加驱动与处理器详见第九节。三、Content Part Driver展示、编辑与提交的三段式桥梁Driver驱动是 Part 与 UI 之间的桥梁负责三个动作渲染展示形状Display、渲染编辑表单Edit、接收并落库表单数据UpdateAsync。一个典型驱动继承自ContentPartDisplayDriverTPartusing OrchardCore.ContentManagement.Display.ContentDisplay; using OrchardCore.ContentManagement.Display.Models; using OrchardCore.DisplayManagement.Views; using OrchardCore.YourModule.Models; using OrchardCore.YourModule.ViewModels; namespace OrchardCore.YourModule.Drivers; public sealed class YourPartDisplayDriver : ContentPartDisplayDriverYourPart { public override IDisplayResult Display(YourPart part, BuildPartDisplayContext context) { return InitializeYourPartViewModel(YourPart, model { model.Title part.Title; model.Description part.Description; }).Location(Detail, Content:5); } public override IDisplayResult Edit(YourPart part, BuildPartEditorContext context) { return InitializeYourPartViewModel(YourPart_Edit, model { model.Title part.Title; model.Description part.Description; model.IsEnabled part.IsEnabled; }); } public override async TaskIDisplayResult UpdateAsync( YourPart part, UpdatePartEditorContext context) { var viewModel new YourPartViewModel(); await context.Updater.TryUpdateModelAsync(viewModel, Prefix); part.Title viewModel.Title; part.Description viewModel.Description; part.IsEnabled viewModel.IsEnabled; return Edit(part, context); } }关键细节说明Display中的InitializeYourPartViewModel(YourPart, ...)创建名为YourPart的形状Shape对应 Razor 视图YourPart.cshtml.Location(Detail, Content:5)把形状放置在Detail显示类型的Content区域内、排序权重为 5Edit返回YourPart_Edit形状对应编辑器视图YourPart_Edit.cshtmlUpdateAsync通过context.Updater.TryUpdateModelAsync(viewModel, Prefix)做模型绑定——Prefix由框架自动设置为 Part 名称保证同名属性在多个 Part 共存时互不冲突绑定成功后直接回写 Part 属性并复用Edit返回编辑形状作为提交后的回显。源码印证驱动基类 ContentPartDisplayDriverTPart.csContentPartDisplayDriverTPart要求TPart : ContentPart, new()内部实现了BuildDisplayAsync/BuildEditorAsync/UpdateEditorAsync三个接口方法它们把 Part 转换为TPart后调用你的重写方法并在更新完成后执行part.ContentItem.Apply(typePartDefinition.Name, part)将数据写回内容项。此外该基类还会自动为形状注入基于ContentTypePartDefinition的 Alternates备选形状名如YourPart-BlogPost并处理Prefix的临时切换与还原这些你无需手写。驱动与 Part 的绑定通过UseDisplayDriverT()完成。其实现位于 ContentPartServiceCollectionExtensions.csUseDisplayDriver会同时调用ForDisplayMode与ForEditor注册驱动并且每次调用都会覆盖同类型驱动的旧注册可安全地多次调用以重配置——这为用派生驱动覆盖默认驱动提供了官方支持的扩展点。四、Content Field附着在 Part 之上的轻量字段Content Field 是比 Part 更轻量的数据单元通常作为 Part 内部的细粒度字段使用同样继承自OrchardCore.ContentManagement.ContentFieldusing OrchardCore.ContentManagement; namespace OrchardCore.YourModule.Fields; public class YourField : ContentField { public string Value { get; set; } public string[] Tags { get; set; } }与 Part 的差异Part 直接挂载在内容类型上而 Field 挂载在 Part 内如产品 Part内嵌价格 Field因此字段的展示/编辑驱动继承自ContentFieldDisplayDriverTField对应 ContentFieldServiceCollectionExtensions.cs 中的注册扩展驱动结构Display/Edit/UpdateAsync 三段式与 Part Driver 完全对称。YourFieldDisplayDriver同样通过Initialize返回形状、通过Updater.TryUpdateModelAsync接收表单数据。从源码结构看AddContentFieldT()与AddContentPartT()在 ServiceCollectionExtensions.cs 中成对出现均通过ConfigureContentOptions登记类型并返回对应的 Option BuilderContentFieldOptionBuilder.cs字段处理器AddHandler/RemoveHandler的注册机制也与 Part 保持一致。五、YesSql 迁移建表、加索引与版本升级模块的数据库结构通过继承DataMigration的迁移类描述。Orchard Core 使用 YesSql ORM迁移方法的命名约定决定了执行顺序先执行Create或CreateAsync再按版本号依次执行UpdateFrom1、UpdateFrom2…… 每个方法返回下一个版本号。using OrchardCore.Data.Migration; using YesSql.Sql; namespace OrchardCore.YourModule; public sealed class Migrations : DataMigration { public async Taskint CreateAsync() { await SchemaBuilder.CreateMapIndexTableAsyncYourIndex(table table .Columnstring(DocumentId, col col.WithLength(26)) .Columnstring(Name, col col.WithLength(255)) .Columnbool(IsEnabled) ); await SchemaBuilder.AlterIndexTableAsyncYourIndex(table table .CreateIndex(IDX_YourIndex_DocumentId, DocumentId) ); return 1; } public async Taskint UpdateFrom1Async() { await SchemaBuilder.AlterIndexTableAsyncYourIndex(table table .AddColumnDateTime(CreatedUtc) ); return 2; } }要点解读CreateMapIndexTableAsyncTIndex依据索引类的属性自动建表Column可链式指定长度WithLength(255)等约束CreateIndex为表添加数据库级索引命名建议使用IDX_IndexName_Column的可读格式UpdateFrom1Async展示平滑升级向既有表追加CreatedUtc列后返回版本号 2后续版本继续以UpdateFrom2Async递增SchemaBuilder由框架注入见 DataMigration.cs 中的public ISchemaBuilder SchemaBuilder { get; set; }其类注释明确说明DataMigration通过方法反射被发现并按Create()→UpdateFromX()的顺序串行执行同时支持同步/异步两种方法形态Create与CreateAsync、UpdateFrom1与UpdateFrom1Async。六、索引定义与 IndexProvider把文档映射为可查询行索引类描述从内容文档中抽取哪些字段进入数据库行IndexProviderTDocument则负责在文档写入时执行抽取映射。二者配合迁移类中的建表语句共同构成 YesSql 的查询基础。using YesSql.Indexes; namespace OrchardCore.YourModule.Indexes; public class YourIndex : MapIndex { public string DocumentId { get; set; } public string Name { get; set; } public bool IsEnabled { get; set; } } public class YourIndexProvider : IndexProviderYourDocument { public override void Describe(DescribeContextYourDocument context) { context.ForYourIndex() .Map(doc new YourIndex { DocumentId doc.Id, Name doc.Name, IsEnabled doc.IsEnabled, }); } }说明MapIndex的字段类型需与迁移类中CreateMapIndexTableAsync的Column声明一一对应string/bool/DateTime等否则运行期会暴露结构不一致Describe中的Map委托在每次文档保存时执行产出索引行DocumentId通常取文档主键doc.Id用于反查实际模块中YourDocument通常是自定义的内容文档类型映射逻辑可包含计算字段或派生值。注册方式为services.AddIndexProviderYourIndexProvider()对应扩展位于 src/OrchardCore/OrchardCore.Data.YesSql.Abstractions/IndexServiceCollectionExtensions.cs该目录还包含IScopedIndexProvider接口用于作用域级索引提供者场景。七、权限提供者声明模块权限并绑定默认角色IPermissionProvider声明模块自定义权限并为其指定默认授予的角色。权限对象支持隐式授权数组拥有某个权限即自动拥有其依赖权限。using OrchardCore.Security.Permissions; namespace OrchardCore.YourModule; public sealed class PermissionProvider : IPermissionProvider { public static readonly Permission ManageYourFeature new(ManageYourFeature, Manage your feature); public static readonly Permission ViewYourFeature new(ViewYourFeature, View your feature, [ManageYourFeature]); public TaskIEnumerablePermission GetPermissionsAsync() Task.FromResultIEnumerablePermission([ManageYourFeature, ViewYourFeature]); public IEnumerablePermissionStereotype GetDefaultStereotypes() [ new PermissionStereotype { Name OrchardCoreConstants.Roles.Administrator, Permissions [ManageYourFeature], }, new PermissionStereotype { Name OrchardCoreConstants.Roles.Editor, Permissions [ViewYourFeature], }, ]; }要点Permission构造器的三个参数分别是权限名称唯一标识形如ManageYourFeature、本地化显示文本、以及隐式权限数组——此处ViewYourFeature隐式包含ManageYourFeature即管理员能管理自然能查看GetPermissionsAsync()返回模块声明的全部权限供管理界面枚举勾选GetDefaultStereotypes()定义默认角色模板Administrator默认获得ManageYourFeatureEditor默认获得ViewYourFeature。OrchardCoreConstants.Roles常量定义于 OrchardCoreConstants.cs注册扩展为services.AddPermissionProviderPermissionProvider()。模块级权限的典型应用见 src/OrchardCore.Modules/OrchardCore.Security 及仓库中各模块的*Permissions.cs文件例如src/OrchardCore.Modules/OrchardCore.Contents中的权限体系。八、Admin 菜单把模块入口注入后台导航AdminNavigationProvider负责向后台管理导航构建器追加菜单节点。它使用IStringLocalizer保证菜单文本可翻译并通过.Permission(...)限定可见性。using Microsoft.Extensions.Localization; using OrchardCore.Navigation; namespace OrchardCore.YourModule; public sealed class AdminMenu : AdminNavigationProvider { private readonly IStringLocalizer S; public AdminMenu(IStringLocalizerAdminMenu localizer) { S localizer; } protected override ValueTask BuildAsync(NavigationBuilder builder) { builder .Add(S[Your Menu], NavigationConstants.AdminMenuYourModulePriority, menu menu .AddClass(your-module) .Id(yourmodule) .Add(S[Your Item], S[Your Item].PrefixPosition(), item item .Action(Index, Admin, OrchardCore.YourModule) .Permission(PermissionProvider.ManageYourFeature) .LocalNav() ) ); return ValueTask.CompletedTask; } }说明Add(S[Your Menu], priority, ...)在导航树中创建一级菜单NavigationConstants.AdminMenuYourModulePriority用于控制菜单排序位置.AddClass(your-module)与.Id(yourmodule)提供样式钩子与锚点二级菜单项通过.Action(Index, Admin, OrchardCore.YourModule)指向模块的AdminController.Index路由area 为OrchardCore.YourModule.Permission(PermissionProvider.ManageYourFeature)将菜单项与权限绑定无权限用户不会看到入口.LocalNav()标记该项为当前区域导航高亮候选注册方式为services.AddNavigationProviderAdminMenu()。导航基础设施位于 src/OrchardCore/OrchardCore.Navigation.Core完整的导航 API含PrefixPosition、LocalNav等扩展可在此目录查看。九、Content Handler订阅内容生命周期事件ContentHandlerBase提供内容项生命周期钩子模块可在内容创建、发布、删除等时刻挂接副作用逻辑如同步索引、发送通知、更新关联数据。原文档给出的事件骨架using OrchardCore.ContentManagement.Handlers; namespace OrchardCore.YourModule.Handlers; public sealed class YourContentHandler : ContentHandlerBase { public override Task PublishedAsync(PublishContentContext context) { // Handle content published event return Task.CompletedTask; } public override Task CreatedAsync(CreateContentContext context) { // Handle content created event return Task.CompletedTask; } public override Task RemovedAsync(RemoveContentContext context) { // Handle content removed event return Task.CompletedTask; } }说明事件上下文PublishContentContext、CreateContentContext、RemoveContentContext等携带ContentItem与当前ContentItemVersion可在钩子内读取或修改内容钩子返回Task可安全地执行异步操作如写数据库、调外部服务除创建/发布/删除外ContentHandlerBase还暴露加载、更新、克隆、草稿等阶段的事件完整定义位于 src/OrchardCore/OrchardCore.ContentManagement.Abstractions/Handlers该目录含ContentHandlerBase、各*Context类型与IContentHandler接口注册方式为services.AddContentHandlerYourContentHandler()若只想把处理器绑定到某个具体 Part/Field可改用AddContentPartT().AddHandlerT()链式 API见 ServiceCollectionExtensions.cs。十、Background Task按 Cron 调度的后台作业后台任务实现IBackgroundTask接口并用[BackgroundTask]特性声明调度计划Cron 表达式与描述using OrchardCore.BackgroundTasks; namespace OrchardCore.YourModule; [BackgroundTask(Schedule */15 * * * *, Description Runs every 15 minutes)] public sealed class YourBackgroundTask : IBackgroundTask { public Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) { // Background work implementation return Task.CompletedTask; } }说明Schedule采用标准 Cron 五段式表达式分 时 日 月 周*/15 * * * *表示每 15 分钟执行一次特性还支持Enable、LockTimeout等配置项可在管理界面动态启停DoWorkAsync每次调度触发时执行通过serviceProvider按需解析 scoped 服务后台任务是单例生命周期依赖解析须从serviceProvider现场创建作用域并应监听cancellationToken实现优雅退出抽象与接口定义位于 src/OrchardCore/OrchardCore.Abstractions/BackgroundTasks任务调度宿主实现在 src/OrchardCore.Modules/OrchardCore.BackgroundTasks该模块还提供后台任务的启用、锁定与运行状态管理。十一、Startup 服务注册把全部构件接入容器上述所有构件最终都要在模块的Startup.ConfigureServices即Startup类的ConfigureServices重写中逐一注册。原文档给出的完整注册清单如下public override void ConfigureServices(IServiceCollection services) { // Content part services.AddContentPartYourPart() .UseDisplayDriverYourPartDisplayDriver(); // Content field services.AddContentFieldYourField() .UseDisplayDriverYourFieldDisplayDriver(); // Services services.AddScopedIYourService, YourService(); // Migrations services.AddDataMigrationMigrations(); // Index provider services.AddIndexProviderYourIndexProvider(); // Permissions services.AddPermissionProviderPermissionProvider(); // Navigation services.AddNavigationProviderAdminMenu(); // Handlers services.AddContentHandlerYourContentHandler(); }逐行解读与源码对照AddContentPartYourPart().UseDisplayDriverYourPartDisplayDriver()登记 Part 类型写入ContentOptions见 ServiceCollectionExtensions.cs再链式把驱动同时注册进展示模式与编辑器ContentPartServiceCollectionExtensions.cs 中的UseDisplayDriver等价于ForDisplayMode(...).ForEditor(...)该链可继续追加.AddHandlerT()、.RemoveHandlerT()等AddContentFieldYourField().UseDisplayDriverYourFieldDisplayDriver()字段登记与驱动注册机制与 Part 对称AddScopedIYourService, YourService()模块自有业务服务按标准 DI 生命周期注册scoped 是 Orchard Core 模块服务的常见选择与请求/租户作用域匹配AddDataMigrationMigrations()注册迁移类由数据迁移系统按版本号串行执行迁移基类见 DataMigration.cs扩展位于 MigrationServiceCollectionExtensions.csAddIndexProviderYourIndexProvider()注册索引提供者让文档写入时自动维护索引行AddPermissionProviderPermissionProvider()注册权限声明管理界面据此渲染权限勾选项AddNavigationProviderAdminMenu()注册后台导航构建器注入模块菜单AddContentHandlerYourContentHandler()注册内容生命周期处理器。补充说明AddDataMigration、AddIndexProvider等扩展方法分散于 src/OrchardCore/OrchardCore.Data.YesSql.Abstractions 目录下AddPermissionProvider、AddNavigationProvider则分别由权限与导航基础设施提供。这些注册均可在模块的Manifest模块清单与Startup结构之外按需组合——不是每个模块都要用到全部构件本文列出的正是内容模块最典型的完整组合。十二、落地清单从模式到可运行模块将上述模式落地为一个新模块时建议按以下顺序推进在src/OrchardCore.Modules下创建模块项目如OrchardCore.YourModule编写模块Manifest与Startup定义Models/YourPart.cs或Fields/YourField.cs确定数据形状编写Migrations与Indexes/YourIndex.cs、YourIndexProvider.cs规划查询所需的表与索引编写Drivers/YourPartDisplayDriver.cs与对应的YourPart.cshtml、YourPart_Edit.cshtml视图形状名与视图文件名严格对应按需添加PermissionProvider、AdminMenu、ContentHandler、BackgroundTask在ConfigureServices中按第十一节清单完成全部注册启用模块后在后台内容类型管理中将 Part/Field 挂载到目标内容类型即可在编辑器中看到表单、在前端看到展示形状。仓库中已落地的大量模块可作为参照实现例如 src/OrchardCore.Modules/OrchardCore.ContentsPart/Driver/Handler 综合示例、src/OrchardCore.Modules/OrchardCore.Alias轻量字段 索引示例、src/OrchardCore.Modules/OrchardCore.BackgroundTasks后台任务宿主以及 src/OrchardCore/OrchardCore.Data.YesSql.Abstractions迁移/索引抽象。对照这些真实代码能进一步确认本文各模式在完整业务模块中的组合方式。赞分享CMS后端Web框架【免费下载链接】OrchardCoreOrchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.项目地址https://gitcode.com/gh_mirrors/or/OrchardCore点击查看免费下载相关推荐Orchard Core 模块开发实战五个可直接套用的完整模块示例Orchard Core 模块开发实战五个可直接套用的完整模块示例 本篇技术指南基于 Orchard Core 仓库内 orchardcore moduleCMS后端Web框架Orchard Core Content Preview 模块实战指南内容编辑器实时预览的完整实现与深度原理Orchard Core Content Preview 模块实战指南内容编辑器实时预览的完整实现与深度原理 导读 本文以 Orchard Core 官方文档CMS后端Web框架axe-core 代码审查模式实战指南从常见反模式到审查者偏好的完整 PR 规范axe core 代码审查模式实战指南从常见反模式到审查者偏好的完整 PR 规范 axe coreAccessibility engine for auto测试创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网