☰
Orchard Core 内容定义存储(Content Definition Store)完全指南:从文件存储到数据库的迁移实战
2026/9/28 21:09:28 网站建设 项目流程
  • CMS
  • 后端
  • Web框架

【免费下载链接】OrchardCore

Orchard 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
点击查看免费下载

Content Definitions(内容定义)是 Orchard Core 多租户架构下描述「Content Types(内容类型)、Content Parts(内容部件)、Content Fields(内容字段)」的元数据记录,它决定了一个租户里可以创建什么样的内容、每个内容由哪些部件和字段构成。本指南围绕 src/docs/guides/content-definitions/README.md 的核心脉络展开,先讲清默认的数据库存储与可选的File Content Definition文件存储两种模式各自的适用场景,再手把手演示如何通过 Deployment Plan(部署计划)把ContentDefinition.json文件中的定义平滑迁移回数据库,最后结合源码揭示底层存储契约与部署步骤的实现原理。读完本文,你将掌握内容定义存储的切换思路、完整迁移流程,并能理解背后的IContentDefinitionStore抽象与部署步骤机制。

一、什么是 Content Definitions

在 Orchard Core 中,Content Definitions是某个租户(tenant)所使用的Content Types、Content Parts与Content Fields的完整记录。它们是内容建模的「蓝图」:例如你在后台把Article类型挂上TitlePart、HtmlBodyPart,添加TextField等操作,最终都会落成一组结构化定义,持久化到租户对应的存储中。

  • Content Type Definition:定义一个内容类型(如Article、Page),包括它挂载了哪些部件(Parts)与字段(Fields),以及可用的编辑器、显示设置等。
  • Content Part Definition:定义可复用的部件(如TitlePart),部件可以被多个内容类型引用。
  • Content Field Definition:定义字段(如TextField、NumericField),字段通常附加在部件或内容类型之上。

从数据结构看,这些定义被统一收纳进一个ContentDefinitionRecord文档对象中,其中包含ContentTypeDefinitionRecords与ContentPartDefinitionRecords两个列表,参见 ContentDefinitionRecord.cs:

[FileDocumentStore(FileName = "ContentDefinition")] public class ContentDefinitionRecord : Document { public IList<ContentTypeDefinitionRecord> ContentTypeDefinitionRecords { get; set; } public IList<ContentPartDefinitionRecord> ContentPartDefinitionRecords { get; set; } }

注意这个类上标注的[FileDocumentStore(FileName = "ContentDefinition")]特性——这正是文件存储模式下文件名的由来(文件名前缀ContentDefinition,序列化后即ContentDefinition.json)。

二、两种存储方式:数据库存储与文件存储

1. 默认方式:数据库存储(Database Content Definition Store)

默认情况下,Content Definitions 存储在数据库(YesSql 文档表)中。这是生产环境的推荐做法:

  • 定义与站点数据一起落在数据库,天然支持事务与备份体系;
  • 多租户场景下,每个租户的定义独立存储、互不干扰;
  • 运行时通过内存缓存加速读取,避免频繁访问数据库。

其实现类为 DatabaseContentDefinitionStore.cs,它通过IDocumentManager<ContentDefinitionRecord>完成文档的读取与更新:

public class DatabaseContentDefinitionStore : IContentDefinitionStore { private readonly IDocumentManager<ContentDefinitionRecord> _documentManager; public Task<ContentDefinitionRecord> LoadContentDefinitionAsync() => _documentManager.GetOrCreateMutableAsync(); public Task<ContentDefinitionRecord> GetContentDefinitionAsync() => _documentManager.GetOrCreateImmutableAsync(); public Task SaveContentDefinitionAsync(ContentDefinitionRecord contentDefinitionRecord) => _documentManager.UpdateAsync(contentDefinitionRecord); }

2. 可选方式:文件存储(File Content Definition Feature)

当启用名为File Content Definition的功能特性后,Content Definitions 不再写入数据库,而是保存在每个租户App_Data目录根部的ContentDefinition.json文件中。以默认租户为例,路径为:

App_Data/Sites/Default/ContentDefinition.json

其实现类为 FileContentDefinitionStore.cs,唯一区别在于它使用的文档管理器绑定的是文件文档存储:

public class FileContentDefinitionStore : IContentDefinitionStore { private readonly IDocumentManager<IFileDocumentStore, ContentDefinitionRecord> _documentManager; public Task<ContentDefinitionRecord> LoadContentDefinitionAsync() => _documentManager.GetOrCreateMutableAsync(); public Task<ContentDefinitionRecord> GetContentDefinitionAsync() => _documentManager.GetOrCreateImmutableAsync(); public Task SaveContentDefinitionAsync(ContentDefinitionRecord contentDefinitionRecord) => _documentManager.UpdateAsync(contentDefinitionRecord); }

从源码可以清晰地看到:文件存储与数据库存储只是「落盘介质」不同,对外暴露的接口完全一致——两者都实现了IContentDefinitionStore接口。该接口在 IContentDefinitionStore.cs 中定义了三个方法:

  • LoadContentDefinitionAsync():加载用于更新的可变定义(不应缓存);
  • GetContentDefinitionAsync():从缓存获取用于共享的不可变定义;
  • SaveContentDefinitionAsync(record):更新存储并刷新缓存。

而切换两种存储的关键,在于依赖注入时注册哪个实现。参见 ServiceCollectionExtensions.cs 中的AddFileContentDefinitionStore:

public static IServiceCollection AddFileContentDefinitionStore(this IServiceCollection services) { services.RemoveAll<IContentDefinitionStore>(); services.AddScoped<IContentDefinitionStore, FileContentDefinitionStore>(); return services; }

该方法先移除默认的数据库实现,再注册文件实现。它由 OrchardCore.Contents 模块的 Startup.cs 中带[Feature("OrchardCore.Contents.FileContentDefinition")]特性的FileContentDefinitionStartup在ConfigureServices阶段调用:

[Feature("OrchardCore.Contents.FileContentDefinition")] public sealed class FileContentDefinitionStartup : StartupBase { public override void ConfigureServices(IServiceCollection services) { services.AddFileContentDefinitionStore(); } }

这就是「启用/禁用功能特性即切换存储」的底层原理:功能启用时注册文件实现,功能禁用时回归默认的数据库实现。

3. 两种存储的定位差异

对比维度数据库存储(默认)文件存储(File Content Definition)
数据落盘位置租户数据库(YesSql 文档表)App_Data/Sites/<租户名>/ContentDefinition.json
适用阶段生产(Production)开发(Development)
主要优势与站点数据统一管理,适合线上运行文件可进版本控制,便于代码评审与 diff 对比
切换方式禁用File Content Definition特性即回退启用OrchardCore.Contents.FileContentDefinition特性

官方文档给出的定位非常明确:文件存储模式在项目的开发(Development)阶段非常有用——你可以把ContentDefinition.json纳入 Git 等版本控制系统,团队成员改动内容建模后能直观地看到 diff,评审更轻松;当站点进入**生产(Production)**阶段,则建议禁用该特性,把 Content Definitions 存回数据库,让定义与线上数据保持一致并由数据库统一管理。

三、为什么开发阶段推荐文件存储

内容定义的每一次改动(新增类型、挂载部件、调整字段设置)都会改写这组元数据。如果全部存进数据库:

  • 改动不可见,无法通过代码评审逐行审查「谁改了什么」;
  • 难以把一套精心设计的内容模型随代码一起分发给其他环境。

而文件存储让内容模型变得「像代码一样可评审、可复用」:

  • ContentDefinition.json是纯文本 JSON,可以直接放进版本库;
  • 拉取代码后,新的开发环境启动即可获得与团队一致的内容模型;
  • 冲突、回滚、分支合并都能用常规 Git 流程处理。

需要注意的是,文件存储在运行期同样会被内存缓存兜底(ContentDefinitionManager维护类型/部件定义的缓存字典),频繁读取不会直接打文件系统;同时,ContentDefinitionManager通过LoadContentDefinitionAsync(可变)与GetContentDefinitionAsync(不可变)两条路径区分「编辑中」与「只读共享」两种使用场景,参见 ContentDefinitionManager.cs。

四、迁移实战:把 ContentDefinition.json 迁回数据库

当你完成开发、准备把站点推向生产时,需要把ContentDefinition.json中的定义迁移进数据库。官方推荐使用Deployment Plan(部署计划)作为迁移载体,整个过程分三步,下面逐一展开。

Step One:创建部署计划并导出定义

在管理后台执行如下操作:

  1. 进入Tools -> Deployments -> Plans菜单;
  2. 点击Add Deployment Plan,新建一个部署计划;
  3. 将部署计划命名为Content Definitions(建议保持这个名称,便于识别);
  4. 选中Content Definitions部署计划;
  5. 点击Add Step(添加步骤);
  6. 选择Update Content Definitions(更新内容定义)步骤;
  7. 勾选Include all content types and parts definitions.(包含全部内容类型与部件定义)复选框;
  8. 执行该部署计划,并在执行时选择File Download Target(文件下载目标)。

执行完成后,浏览器会下载一个名为ContentDefinitions.zip的文件到本地。这个压缩包内即包含了当前文件存储里的全部内容定义(快照)。

这一步背后的部署步骤实现是 ContentDefinitionDeploymentStep.cs。从源码可以看到它的两个关键配置项:

public class ContentDefinitionDeploymentStep : DeploymentStep { public bool IncludeAll { get; set; } // 是否包含全部内容类型与部件 public string[] ContentTypes { get; set; } // 指定的内容类型列表 public string[] ContentParts { get; set; } // 指定的内容部件列表 }
  • 勾选「包含全部内容类型和部件定义」对应IncludeAll = true,导出时不做筛选;
  • 若不勾选,则可通过ContentTypes/ContentParts两个数组有选择地导出部分定义;
  • 该步骤的Name为ContentDefinition,界面上显示的中文标题Update Content Definitions由IStringLocalizer提供(源码中Title = S["Update Content Definitions"],分类归入Content Management)。

Step Two:禁用 File Content Definition 特性

在把定义导入数据库之前,需要先让系统「回到数据库存储」:

  1. 进入Tools -> Features菜单;
  2. 找到名为File Content Definition的功能特性;
  3. 禁用该特性。

如前文所述,禁用该特性后,FileContentDefinitionStartup不再注册,依赖注入容器中的IContentDefinitionStore恢复为默认的DatabaseContentDefinitionStore实现(参见 ServiceCollectionExtensions.cs 与 Startup.cs)。

提示:此时站点处于「定义已从文件移除、尚未写入数据库」的过渡窗口,建议在低峰期操作,并确认已导出ContentDefinitions.zip且文件完好。

Step Three:导入部署包

最后把第一步导出的文件导回系统:

  1. 进入Tools -> Deployments -> Package Import菜单;
  2. 选择第一步下载的ContentDefinitions.zip文件;
  3. 点击Import(导入)。

导入过程会执行部署计划中的Update Content Definitions步骤,把IncludeAll覆盖的全部内容类型与部件定义写回当前存储——此时当前存储已是数据库,因此定义便成功落入了数据库。

迁移完成后的校验

导入完成后建议做如下检查:

  • 在后台Content -> Content Types中确认各内容类型及其部件、字段完整无缺;
  • 新建一个示例内容项,验证字段编辑、预览等行为与迁移前一致;
  • 确认App_Data/Sites/Default/下不再生成或更新ContentDefinition.json(特性已禁用)。

五、迁移原理小结:为什么部署计划能胜任这件事

整个迁移流程之所以成立,是因为 Orchard Core 的部署体系本身就是「跨环境搬运配置」的标准通道:

  1. 导出阶段:部署计划的ContentDefinitionDeploymentStep从当前存储(文件)读取定义,打包成ContentDefinitions.zip;
  2. 切换阶段:禁用特性后,IContentDefinitionStore实现从FileContentDefinitionStore切换为DatabaseContentDefinitionStore;
  3. 导入阶段:Package Import 执行部署步骤,将定义写入新的存储(数据库),完成介质迁移。

由于FileContentDefinitionStore与DatabaseContentDefinitionStore都实现同一个 IContentDefinitionStore 契约(Load / Get / Save 三方法),导入逻辑无需感知底层介质差异,这也是「文件→数据库」可以无缝迁移的根本原因。

六、总结

  • Content Definitions是租户级的内容建模元数据,包含类型、部件与字段三类定义,统一存放在ContentDefinitionRecord文档中;
  • 默认存数据库;启用File Content Definition特性后改存App_Data/Sites/<租户>/ContentDefinition.json,开发阶段便于版本控制与评审,生产阶段建议存数据库;
  • 两种介质共享IContentDefinitionStore抽象,切换本质是依赖注入实现的替换(AddFileContentDefinitionStore);
  • 迁移三部曲:创建部署计划导出ContentDefinitions.zip→ 禁用File Content Definition特性 → 通过 Package Import 导入部署包;
  • 部署步骤 ContentDefinitionDeploymentStep 支持IncludeAll全量导出或按ContentTypes/ContentParts选择性导出,迁移时按需勾选即可。

掌握了这套流程,你就能在「开发期用文件管理内容模型、上线前平滑切回数据库」之间自由切换,让内容建模既保持代码级可评审性,又具备生产环境的稳定性。

  • CMS
  • 后端
  • Web框架

【免费下载链接】OrchardCore

Orchard 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
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询