运行时数据库迁移
本文档引用文件
/lmes-template/src/CMS.Plugin.MyPluginName/CMSPluginRuntimeMigrator.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/Data/CMSPluginDbMigrationService.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/Data/ICMSPluginDbSchemaMigrator.cs/lmes-template/src/CMS.Plugin.MyPluginName.EntityFrameworkCore/CMSPluginDbSchemaMigrator.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/CMSPluginDbProperties.cs/lmes-template/src/CMS.Plugin.MyPluginName.EntityFrameworkCore/ICMSPluginDbContext.cs/lmes-template/src/CMS.Plugin.MyPluginName.EntityFrameworkCore/CMSPluginEntityFrameworkCoreModule.cs/lmes-template/src/CMS.Plugin.MyPluginName/CMSPluginModule.cs
目录
引言
本文深入解析CMS插件中CMSPluginRuntimeMigrator类如何实现工程级运行时数据库迁移功能。该机制在项目加载时自动触发,确保插件数据库结构与代码模型保持同步。系统基于ABP框架构建,结合Entity Framework Core的迁移能力,实现了安全、可扩展的数据库演进方案。
核心组件分析
CMSPluginRuntimeMigrator作为IProjectRuntimeMigrator接口的具体实现,在项目运行时承担数据库初始化和迁移职责。其核心逻辑通过依赖注入获取CMSPluginDbMigrationService并执行异步迁移操作。
图示来源
/lmes-template/src/CMS.Plugin.MyPluginName/CMSPluginRuntimeMigrator.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/Data/CMSPluginDbMigrationService.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/Data/ICMSPluginDbSchemaMigrator.cs/lmes-template/src/CMS.Plugin.MyPluginName.EntityFrameworkCore/CMSPluginDbSchemaMigrator.cs
本节来源
/lmes-template/src/CMS.Plugin.MyPluginName/CMSPluginRuntimeMigrator.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/Data/CMSPluginDbMigrationService.cs
运行时迁移流程
当IProjectRunner加载工程时,CMSPluginRuntimeMigrator的UpgradeAsync方法被调用。此过程接收当前项目实例和包含运行时上下文的服务提供器(IServiceProvider)。
迁移流程如下:
- 将当前项目实例注入
ObjectAccessor<Project.Project>,供后续服务访问 - 获取日志记录器用于追踪迁移过程
- 通过服务提供器获取
CMSPluginDbMigrationService并调用其MigrateAsync方法 - 异常捕获确保迁移失败不会中断主流程
- 清理项目上下文对象
图示来源
/lmes-template/src/CMS.Plugin.MyPluginName/CMSPluginRuntimeMigrator.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/Data/CMSPluginDbMigrationService.cs/lmes-template/src/CMS.Plugin.MyPluginName.EntityFrameworkCore/CMSPluginDbSchemaMigrator.cs
本节来源
/lmes-template/src/CMS.Plugin.MyPluginName/CMSPluginRuntimeMigrator.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/Data/CMSPluginDbMigrationService.cs
数据库上下文与迁移历史管理
数据库上下文通过ICMSPluginDbContext接口定义,并使用[ConnectionStringName]特性指定连接字符串名称。该接口继承自ABP的IEfCoreDbContext,确保与框架集成。
迁移历史表由常量CMSPluginDbProperties.MigrationsHistoryTable定义,值为__EFMigrationsHistoryForMyPluginName。此自定义表名避免了多模块间迁移历史冲突,是ABP模块化设计的关键实践。
图示来源
/lmes-template/src/CMS.Plugin.MyPluginName.Domain/CMSPluginDbProperties.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/Data/CMSPluginDbMigrationService.cs/lmes-template/src/CMS.Plugin.MyPluginName.EntityFrameworkCore/ICMSPluginDbContext.cs
本节来源
/lmes-template/src/CMS.Plugin.MyPluginName.Domain/CMSPluginDbProperties.cs/lmes-template/src/CMS.Plugin.MyPluginName.EntityFrameworkCore/ICMSPluginDbContext.cs
依赖注入与服务协作机制
系统采用ABP框架的依赖注入容器进行服务管理。CMSPluginDbMigrationService构造函数注入IEnumerable<ICMSPluginDbSchemaMigrator>,实现策略模式,支持多个数据库迁移器并行执行。
CMSPluginDbSchemaMigrator通过IServiceProvider延迟解析ICMSPluginDbContext,确保获取当前租户的正确连接字符串。这种设计特别适用于多租户场景,避免了直接注入导致的上下文绑定问题。
模块依赖关系如下:
CMSPluginModule依赖CMSPluginEntityFrameworkCoreModuleCMSPluginEntityFrameworkCoreModule依赖CMSPluginDomainModule- 各层模块通过
[DependsOn]特性声明依赖
图示来源
/lmes-template/src/CMS.Plugin.MyPluginName/CMSPluginModule.cs/lmes-template/src/CMS.Plugin.MyPluginName.EntityFrameworkCore/CMSPluginEntityFrameworkCoreModule.cs/lmes-template/src/CMS.Plugin.MyPluginName.EntityFrameworkCore/CMSPluginDbSchemaMigrator.cs
本节来源
/lmes-template/src/CMS.Plugin.MyPluginName/CMSPluginModule.cs/lmes-template/src/CMS.Plugin.MyPluginName.EntityFrameworkCore/CMSPluginEntityFrameworkCoreModule.cs
错误处理与日志记录策略
系统采用结构化异常处理机制,在UpgradeAsync方法中使用try-catch捕获迁移过程中的所有异常。错误信息通过ILogger记录,包含异常堆栈,便于问题排查。
日志级别策略:
Information:记录迁移开始/结束等关键节点Debug:记录内部流程细节,用于调试Warning:记录非致命问题,如无法确定迁移状态Error:记录迁移失败等严重问题
初始迁移创建失败时抛出Exception("Couldn't run ABP CLI..."),确保问题及时暴露。
本节来源
/lmes-template/src/CMS.Plugin.MyPluginName/CMSPluginRuntimeMigrator.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/Data/CMSPluginDbMigrationService.cs
多租户环境下的注意事项
在多租户系统中,CMSPluginDbSchemaMigrator通过IServiceProvider.GetRequiredService()动态获取数据库上下文,确保使用当前租户的连接字符串。这是区别于直接注入的关键设计。
数据种子(Data Seeding)通过DataSeedContext.WithProperty传递连接字符串名称,确保种子数据写入正确的数据库。
迁移历史表使用模块专属名称(__EFMigrationsHistoryForMyPluginName),避免不同模块间的表名冲突,支持独立迁移管理。
本节来源
/lmes-template/src/CMS.Plugin.MyPluginName.EntityFrameworkCore/CMSPluginDbSchemaMigrator.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/Data/CMSPluginDbMigrationService.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/CMSPluginDbProperties.cs
实际调用场景
典型调用流程发生在项目加载阶段:
IProjectRunner创建CMSPluginRuntimeMigrator实例- 传入当前项目和作用域服务提供器
- 执行
UpgradeAsync触发迁移 - 完成后释放资源
此机制确保每次项目运行时数据库结构均为最新,适用于开发、测试和生产环境的自动化部署。
本节来源
/lmes-template/src/CMS.Plugin.MyPluginName/CMSPluginRuntimeMigrator.cs/lmes-template/src/CMS.Plugin.MyPluginName.Domain/Data/CMSPluginDbMigrationService.cs
总结
CMSPluginRuntimeMigrator通过ABP框架的依赖注入机制,协调CMSPluginDbMigrationService和CMSPluginDbSchemaMigrator完成运行时数据库迁移。系统设计充分考虑了模块化、多租户和错误恢复能力,采用专用迁移历史表和动态上下文解析策略,确保在复杂环境下的稳定运行。日志记录和异常处理机制为运维提供了有力支持,是插件化架构中数据库演进的可靠解决方案。