新闻详情

新闻详情

首页 / 资讯中心 / 详情

OrchardCore 数据迁移模式实战:从内容定义调整到存量数据修补的完整指南

发布时间:2026/11/13 1:48:39来源:尧图网络
OrchardCore 数据迁移模式实战:从内容定义调整到存量数据修补的完整指南
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点击查看免费下载导读数据迁移Data Migration是 OrchardCore 模块在安装与升级时修改自身架构的标准手段无论是新增/调整内容类型与部件、修补存量内容项数据、执行 recipe 种子数据还是管理 SQL 索引表都通过继承DataMigration的迁移类完成。本文以 OrchardCore 官方技能文档中的迁移模式patterns为骨架逐类拆解 7 种可直接套用的迁移写法并结合仓库源码DataMigrationManager.cs、OrchardCore.Html/Migrations.cs 等揭示其底层执行机制。读完本文你将能独立编写内容定义类迁移、存量数据修补类迁移与 recipe 类迁移并正确使用版本链规则与分页刷新技巧。一、先理解迁移运行器的版本链机制所有迁移模式都建立在同一个执行模型之上。OrchardCore 通过反射发现迁移类中的方法并按“版本链”顺序逐个执行链条从CreateAsync()开始依次经过UpdateFrom1Async()、UpdateFrom2Async()……每个方法返回的整数是下一个方法必须匹配的版本号。在 DataMigrationManager.cs 中可以看到完整的执行逻辑current 0新租户无记录时调用CreateAsync()随后构建CreateUpgradeLookupTable见 L308-L332该方法按反射收集所有名称以UpdateFrom开头、返回int或Taskint的方法并以方法名中的数字为键建立查找表while (lookupTable.TryGetValue(current, out var methodInfo))循环调用对应版本方法用其返回值驱动下一跳L233-L243最后把返回的版本号持久化到DataMigrationRecordL251下一次启动时据此决定从哪个版本继续。迁移类本身非常轻量——DataMigration基类只暴露一个SchemaBuilder属性DataMigration.cs其余依赖全部通过构造函数注入。版本链的硬性规则规则原因CreateAsync只在全新安装时执行存量租户已经越过该方法不会再跑CreateAsync返回当前最新版本号直接跳过所有UpdateFromX新站点直接获得最终 schema无需逐级升级UpdateFromXAsync仅在存储版本号 X 时执行保证顺序升级路径绝不重新编号已发布的方法其他租户已持久化旧编号重编号会破坏它们的升级路径通常每个UpdateFromX返回X1允许在后续步骤吞并前序步骤时跳号返回更大的值二、内容定义类迁移部件、类型与全局替换内容定义类迁移操作的是“元数据”而非内容数据本身通过IContentDefinitionManager完成。底层AlterPartDefinitionAsync/AlterTypeDefinitionAsync的“读-改-写”流程定义在 ContentDefinitionManagerExtensions.cs先Load现有定义不存在则创建空定义交给 builder 修改再Store持久化。2.1 创建一个部件Part最常见的模式是注册一个新的可挂载部件public async Taskint CreateAsync() { await _contentDefinitionManager.AlterPartDefinitionAsync(HtmlBodyPart, builder builder .Attachable() .WithDescription(Provides an HTML Body for your content item.)); return 6; // 最新版本号全新安装会跳过所有 UpdateFromX }这段代码与 OrchardCore.Html/Migrations.cs 中的真实实现完全一致。Attachable()使该部件可以被附加到任意内容类型WithDescription则提供后台可见的说明文字。注意返回 6 而非 1——一旦模块存在UpdateFromX方法CreateAsync必须返回最高版本号。2.2 创建一个类型Typeawait _contentDefinitionManager.AlterTypeDefinitionAsync(Product, type type .Creatable() // 出现在“新建”菜单中 .Listable() // 显示在内容列表 .Draftable() // 支持草稿 .Versionable() // 保留版本历史 .Securable() // 按类型授权 .WithPart(TitlePart) .WithPart(ProductPart));各 builder 方法的语义.Creatable()决定是否出现在新建菜单、.Listable()决定是否出现在内容列表、.Draftable()支持草稿、.Versionable()保留版本历史、.Securable()启用按类型授权.WithPart(PartName)则将部件挂载到该类型。2.3 在全部类型上替换部件如 BodyPart → HtmlBodyPart当需要重命名/替换一个被多个类型使用的部件时典型场景BodyPart迁移为HtmlBodyPart必须遍历所有类型定义逐个替换最后删除旧部件定义public async Taskint UpdateFrom3Async() { foreach (var contentType in await _contentDefinitionManager.LoadTypeDefinitionsAsync()) { if (contentType.Parts.Any(x x.PartDefinition.Name BodyPart)) { await _contentDefinitionManager.AlterTypeDefinitionAsync(contentType.Name, x x.RemovePart(BodyPart).WithPart(HtmlBodyPart)); } } await _contentDefinitionManager.DeletePartDefinitionAsync(BodyPart); return 4; }仓库中 OrchardCore.Html/Migrations.cs 的UpdateFrom3Async就是这段模式的原始出处。关键区别迁移内必须使用LoadTypeDefinitionsAsync()返回可变定义用于写入而不是ListTypeDefinitionsAsync()返回缓存只读副本。前者供修改后回写后者仅用于读取展示。2.4 迁移部件设置MergeSettings在部件设置模型演进时用WithPart内嵌MergeSettingsT合并现有设置而不要整体覆写public async Taskint UpdateFrom5Async() { foreach (var contentType in await _contentDefinitionManager.LoadTypeDefinitionsAsync()) { if (contentType.Parts.Any(p p.PartDefinition.Name HtmlBodyPart)) { await _contentDefinitionManager.AlterTypeDefinitionAsync(contentType.Name, t t.WithPart(HtmlBodyPart, part { part.MergeSettingsHtmlBodyPartSettings(s s.RenderLiquid !s.SanitizeHtml); })); } } return 6; }这段代码来自 OrchardCore.Html/Migrations.cs当模块把“Liquid 渲染”从“取消 HTML 净化”中独立出来时为保证既有站点行为一致用RenderLiquid !SanitizeHtml完成设置迁移。MergeSettings只更新指定字段、保留其余设置避免误伤自定义配置。三、修补存量内容项数据分页游标 Flush内容定义之外迁移还常需要“动数据”——移动或转换已存储在内容项中的字段。这是所有模式中对正确性要求最高的一个。3.1 为什么必须放在 UpdateFromX 而不是 CreateAsync存量数据修补不是全新安装所必需的新站点还没有任何内容项因此它必须存在于UpdateFromX中绝不能写进CreateAsync——否则每次新装都会无谓地跑一遍空循环。3.2 标准的分页修补循环public async Taskint UpdateFrom1Async() { var lastDocumentId 0L; for (; ; ) { var contentItemVersions await _session .QueryContentItem, ContentItemIndex(x x.DocumentId lastDocumentId) .Take(10) .ListAsync(); if (!contentItemVersions.Any()) { break; // 没有更多待处理版本 } foreach (var contentItemVersion in contentItemVersions) { if (UpdateItem((JsonObject)contentItemVersion.Content)) { await _session.SaveAsync(contentItemVersion); } lastDocumentId contentItemVersion.Id; } await _session.FlushAsync(); } return 2; }3.3 四个关键要点查索引、改文档查询走ContentItemIndexYesSql map 索引修改的对象却是ContentItem文档本体contentItemVersion.Id即 YesSql 的DocumentId充当游标。索引定义见 ContentItemIndex.cs其中DocumentId是 YesSql 自动维护的文档主键。以JsonObject/JsonNode修改 JSONcontentItemVersion.Content是System.Text.Json节点树直接对其就地增删改再SaveAsync持久化。遍历所有版本而非仅最新/已发布这样草稿与历史版本也会一并修补。这一点在真实模块中确实如此——OrchardCore.Html/Migrations.cs 的UpdateFrom3Async正是以“所有版本”为目标把BodyPart.Body迁移为HtmlBodyPart.Html。必须递归嵌套部件内容可能深藏在 Flows / Bag 部件等嵌套结构中修补函数需要递归遍历对象与数组。上述真实模块中的UpdateBody静态局部函数就同时处理了JsonValueKind.Object与JsonValueKind.Array两种节点L98-L127。3.4 为什么按 DocumentId 分页并 Flush按DocumentId lastDocumentId排序分页可以稳定推进避免重复处理每页结束调用_session.FlushAsync()及时落库防止长事务累积内存与锁每批Take(10)控制单次往返的数据量适合内容项较多的站点。四、从迁移中执行 recipe种子数据一键注入迁移可以调用 recipe 完成“配置类”工作——播种模板、查询、图层layers或示例内容无需手写大量 C# 代码。public Migrations(IRecipeMigrator recipeMigrator) _recipeMigrator recipeMigrator; public async Taskint CreateAsync() { await _recipeMigrator.ExecuteAsync(init.recipe.json, this); return 1; }4.1 底层解析路径IRecipeMigrator接口定义在 IRecipeMigrator.cs其实现 RecipeMigrator.cs 揭示了 recipe 文件的查找规则recipe 文件相对模块的Migrations目录解析Path.Combine(extensionInfo.SubPath, Migrations)随后交给IRecipeReader读取、IRecipeExecutor执行并设置RequireNewScope false让迁移与 recipe 共享同一事务作用域。4.2 仓库中的真实案例OrchardCore.AdminDashboard/Migrations.cs 在CreateAsync与UpdateFrom1Async中都调用了_recipeMigrator.ExecuteAsync(dashboard-widgets.recipe.json, this)——升级时也会重新执行该 recipe 以确保 dashboard 部件与图层数据就位。recipe 文件随模块发布通常放在Migrations/或Recipes/目录其步骤格式与安装向导使用的 setup recipe 完全一致。五、版本跳号用返回数字吞并过时步骤当后一个升级步骤已经包含前一个步骤的变更时可以返回高于X1的数字让运行器跳过中间步骤public async Taskint UpdateFrom3Async() { // ... 实际变更 ... return 5; // 返回 5 而非 4因为 UpdateFrom4 已不再需要 }运行器随后会在查找表中寻找UpdateFrom5Async永远不会调用已被移除的UpdateFrom4Async。这正是 OrchardCore.Html/Migrations.cs 中的注释所记载的做法“Returning 5 instead of 4, because UpdateFrom5 is no longer needed”——原本的UpdateFrom4Async被移除UpdateFrom3Async直接返回 5 跳过了它。使用前提只有当你确认“所有可能处于版本 4 的租户升级时都无需执行任何步骤”时才能跳号如果跳过的步骤对部分租户仍有意义则不应移除对应方法。六、迁移方法的形态与注册6.1 方法签名速查方法形态触发时机Createint Create()·Taskint CreateAsync()全新安装Updateint UpdateFromX()·Taskint UpdateFromXAsync()存储版本 XUninstallvoid Uninstall()·Task UninstallAsync()功能被卸载静态或实例方法、同步或异步均可被反射发现DataMigrationManager.cs 对UpdateFrom与Async后缀均做了解析新代码统一采用异步形式...Async。注意SchemaBuilder由基类注入不要把它放进构造函数参数。6.2 注册迁移类在模块Startup.cs中注册services.AddDataMigrationMigrations();真实例子见 OrchardCore.Html/Startup.cs。若模块有多个迁移类例如Migrations/PermissionMigrations.cs每个类需单独注册。6.3 升级流程与测试建议全新安装在干净租户上运行只有CreateAsync执行schema 直接到达最新版本升级从上一版本的数据库启动确认每个UpdateFromX按序执行跨数据库测试在目标支持的每个提供程序SQLite、SQL Server、MySQL、PostgreSQL上验证。SQLite 无法删除列涉及列删除/重建的操作需try/catch包裹详见 schema-builder 参考文档 schema-builder.md。七、常见坑位速查字符串索引列必须设长度.WithLength(n)或.Unlimited()未设长度的字符串列在不同提供程序上默认不一致会导致迁移在部分数据库上失败CreateAsync返回最新版本一旦存在UpdateFromXCreateAsync就不能再返回1修补循环必须按DocumentId分页并周期性FlushAsync()否则大表迁移会累积内存与事务开销过时的UpdateFromX保留但标注用// This code can be removed in a later version.注释标记在放弃该升级路径之前不要删除。八、模式对照总表目标推荐 API / 模式定义/修改内容类型_contentDefinitionManager.AlterTypeDefinitionAsync(...)定义/修改部件_contentDefinitionManager.AlterPartDefinitionAsync(...)移除部件/类型RemovePart(...)、DeletePartDefinitionAsync(...)创建 SQL 索引表SchemaBuilder.CreateMapIndexTableAsyncTIndex(...)增删列或索引SchemaBuilder.AlterIndexTableAsyncTIndex(...)迁移中执行 recipe_recipeMigrator.ExecuteAsync(init.recipe.json, this)修补存量内容项ISession.QueryContentItem, ContentItemIndex(...)SaveAsync本文 7 类模式均可在仓库中找到真实对应实现内容定义类见 OrchardCore.Html/Migrations.cs 与 OrchardCore.AdminDashboard/Migrations.cs版本链机制见 DataMigrationManager.cs索引查询见 ContentItemIndex.cs。更多关于 SQL 索引表、列规则与数据库差异的细节可继续阅读配套参考 schema-builder.md 与技能总览 SKILL.md。赞分享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点击查看免费下载相关推荐OrchardCore 数据迁移实战DataMigration 版本链、SchemaBuilder 与内容修补全指南OrchardCore 数据迁移实战DataMigration 版本链、SchemaBuilder 与内容修补全指南 OrchardCore 作为基于 ASPCMS后端Web框架从 Postgres 迁移数据到 GelEdgeDB完整数据迁移指南从 Postgres 迁移数据到 GelEdgeDB完整数据迁移指南 导读 本指南基于 GelEdgeDB官方文档《Switching to Gel》数据库图数据库关系型数据库RedwoodJS Data Migrations 数据迁移完整指南从结构迁移到内容迁移的工程化实践RedwoodJS Data Migrations 数据迁移完整指南从结构迁移到内容迁移的工程化实践 本指南基于 RedwoodJS 官方文档version后端前端Web框架开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

【外设】之大彩串口显示屏 2026/11/13 0:43:49

【外设】之大彩串口显示屏

大彩串口屏初步使用 1 .官网下载 STM32 屏幕 GUI 设计资料 http://www.gz-dc.com/category/typeid/4112 找到 STM32 Keil 工程,移植相关代码因项目而异进行移植,由于项目简单,本人只对用到的指令接口进行修改。 比如:注意事项&…

阅读更多 →
无法下载Windows系统iso文件 2026/11/12 23:59:09

无法下载Windows系统iso文件

当我遇到这个问题的时候,我打开了一个网站: 登录 然后我打算下载的时候: 突然那个官方的连接就可以下载了:

阅读更多 →
【清华代码熊】DeepSeek V4.1 Flash 后训练详解 2026/11/12 9:50:02

【清华代码熊】DeepSeek V4.1 Flash 后训练详解

📌 上期解析了 DeepSeek V4.1 Flash 模型架构改进,本期解析 DeepSeek V4.1 Flash 预训练/后训练技术: 🌟 预训练:45T 文本 多模态混合语料、直接训练 sparse attention(取消 DeepSeek V4 的 dense 冷启动&…

阅读更多 →
Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ... 2026/11/12 9:25:38

Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ...

文章主要内容和创新点 主要内容 本文聚焦于多模态大语言模型(MLLM)强化学习(RL)训练中的效率问题,提出了一个名为Shuffle-R1的框架。研究发现,当前RL训练存在两个关键缺陷: 优势值坍缩(Advantage Collapsing):批次中大多数优势值集中在零附近,导致有效梯度信号被淹…

阅读更多 →
PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction 2026/11/12 15:57:44

PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction

一、文章主要内容总结 本文聚焦于利用大型语言模型(LLMs)实现个人身份信息(PII)脱敏的研究,旨在解决传统脱敏方法(如基于规则的系统、领域特定命名实体识别(NER)模型)泛化能力差、跨格式/跨语境适应性弱的问题。 研究通过全面评估多种LLM架构(包括密集型LLM(D-LLM…

阅读更多 →
LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model 2026/11/13 0:02:27

LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model

文章主要内容和创新点 主要内容 本文聚焦于二进制图像-文本相关性评估任务(判断图像与文本“相关”或“不相关”),针对该任务中文本格式多样、相关性定义随场景变化等挑战,提出了基于多模态大语言模型(MLLM)的解决方案LLaVA-RE。 模型设计:LLaVA-RE基于LLaVA 1.5架构,…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉