CMS后端【免费下载链接】Umbraco-CMSUmbraco is a free and open source .NET content management system helping you deliver delightful digital experiences.项目地址https://gitcode.com/gh_mirrors/um/Umbraco-CMS点击查看免费下载本文围绕 Umbraco CMS 源码仓库中的Umbraco.Cms.Persistence.EFCore.Sqlite项目类库/NuGet 包目标框架 net10.0系统讲解 SQLite 在 EF Core 持久化层中的落地方式IMigrationProvider/IMigrationProviderSetup的职责与实现、SqliteRetryingExecutionStrategy应对SQLITE_BUSY/SQLITE_LOCKED的容错机制、四条历史迁移文件的具体内容以及新增迁移的标准操作流程。读完本文你将能够理解 Umbraco 中 EF Core 与 NPoco 双持久化栈如何并行工作并掌握为 SQLite 数据库编写、执行、同步 EF Core 迁移的完整方法。一、项目定位为 Umbraco 的 EF Core 层提供 SQLite 专属能力src/Umbraco.Cms.Persistence.EFCore.Sqlite/是一个薄提供程序thin provider项目它不定义新的业务模型而是把父项目Umbraco.Cms.Persistence.EFCore抽象出来的接口按 SQLite 特性落地。从项目文件 Umbraco.Cms.Persistence.EFCore.Sqlite.csproj 可以看出它只引用Microsoft.EntityFrameworkCore.Sqlite包和父项目的ProjectReference。它的四大职责迁移提供程序Migration Provider——执行 SQLite 专属的 EF Core 迁移迁移提供程序设置Migration Provider Setup——把 DbContext 配置为使用 SQLite并挂载瞬时错误重试策略迁移文件Migrations——面向 OpenIddict 数据表的 SQLite 迁移重试执行策略Retrying Execution Strategy——在 EF Core 操作上重试瞬时性的 SQLite 锁错误。与父项目的协作关系实现父项目 IMigrationProviderMigrateAsync/MigrateAllAsync和 IMigrationProviderSetupSetup复用父项目的UmbracoDbContext基类提供程序名取自父项目常量Constants.ProviderNames.SQLLite Microsoft.Data.Sqlite见 Constants-ProviderNames.cs与 SQL Server 的Microsoft.Data.SqlClient形成对照。目录结构与仓库实际文件一一对应src/Umbraco.Cms.Persistence.EFCore.Sqlite/ ├── Migrations/ │ ├── 20230622183638_InitialCreate.cs # No-opNPoco 创建基础表 │ ├── 20230807123456_AddOpenIddict.cs # 创建 OpenIddict 四张表 │ ├── 20240403141051_UpdateOpenIddictToV5.cs # OpenIddict v5 模式更新 │ ├── 20251006140958_UpdateOpenIddictToV7.cs # OpenIddict v7SQLite 下为 no-op │ └── UmbracoDbContextModelSnapshot.cs # 当前模型快照EF Core 自动生成 ├── EFCoreSqliteComposer.cs # DI 注册 ├── SqliteMigrationProvider.cs # IMigrationProvider 实现 ├── SqliteMigrationProviderSetup.cs # IMigrationProviderSetup 实现 └── SqliteRetryingExecutionStrategy.cs # 处理瞬时锁错误的 IExecutionStrategy二、三个核心组件逐个拆解1. EFCoreSqliteComposerDI 注册入口EFCoreSqliteComposer.cs 是 Umbraco 组合器IComposer在启动时把两个 SQLite 实现注册为单例public class EFCoreSqliteComposer : IComposer { public void Compose(IUmbracoBuilder builder) { builder.Services.AddSingletonIMigrationProvider, SqliteMigrationProvider(); builder.Services.AddSingletonIMigrationProviderSetup, SqliteMigrationProviderSetup(); } }这样父项目中的 EFCoreMigrationExecutor 在迁移时就能按提供程序名ProviderName与配置中ConnectionStrings.ProviderName匹配找到正确的实现。值得注意的是EFCoreMigrationExecutor的注释明确写着We need to do migrations outside of a scope due to SQLite——SQLite 下迁移必须在作用域之外执行这正是下一节MigrateAllAsync检查事务的原因。2. SqliteMigrationProvider命名迁移与全量迁移SqliteMigrationProvider.cs 通过IDbContextFactoryUmbracoDbContext创建上下文提供两种迁移入口MigrateAsync(EFCoreMigration)——执行指定的命名迁移先创建 DbContext再调用父项目的扩展方法MigrateDatabaseAsync(GetMigrationType(migration))该扩展定义在 DbContextExtensions.cs其内部读取迁移类上的MigrationAttribute拿到迁移 ID再交给 EF Core 的IMigrator.MigrateAsync。MigrateAllAsync()——执行全部待应用迁移直接调用context.Database.MigrateAsync()。关键约束MigrateAllAsync在事务激活时不能执行。源码在第 36-39 行做了显式防护if (context.Database.CurrentTransaction is not null) { throw new InvalidOperationException(Cannot migrate all when a transaction is active.); }GetMigrationType()的 switch 表达式把 EFCoreMigration 枚举定义在父项目迁移命名空间中映射到具体迁移类型EFCoreMigration.InitialCreate typeof(Migrations.InitialCreate), EFCoreMigration.AddOpenIddict typeof(Migrations.AddOpenIddict), EFCoreMigration.UpdateOpenIddictToV5 typeof(Migrations.UpdateOpenIddictToV5), EFCoreMigration.UpdateOpenIddictToV7 typeof(Migrations.UpdateOpenIddictToV7),枚举定义EFCoreMigrationInitialCreate 0、AddOpenIddict 1、UpdateOpenIddictToV5 2、UpdateOpenIddictToV7 3与 switch 必须保持同步——新增命名迁移时两处都要更新。3. SqliteMigrationProviderSetup配置 UseSqlite 重试策略SqliteMigrationProviderSetup.cs 实现了IMigrationProviderSetup.Setuppublic void Setup(DbContextOptionsBuilder builder, string? connectionString) { builder.UseSqlite(connectionString, x { x.MigrationsAssembly(GetType().Assembly.FullName); x.ExecutionStrategy(deps new SqliteRetryingExecutionStrategy(deps)); }); }两个关键动作MigrationsAssembly指向本程序集确保 SQLite 迁移文件只从这个项目加载ExecutionStrategy挂载自定义的SqliteRetryingExecutionStrategy。从 UmbracoDbContext.cs 的ConfigureOptions可以看到该Setup会被每一个UmbracoDbContext实例在构造时调用先解析IOptionsMonitorConnectionStrings拿到连接串与提供程序名再按提供程序名找到匹配的IMigrationProviderSetup执行配置。这意味着所有经 EF Core 访问 Umbraco 数据库的路径包括 OpenIddict 的令牌存储都会继承这套重试策略。三、SqliteRetryingExecutionStrategy为什么 SQLite 需要自定义重试Microsoft 官方没有为 SQLite 内置执行策略只有 SQL Server 版本因此 Umbraco 自行实现了一个用于解决 issue #22939长迁移期间 OpenIddict 令牌读取失败。问题根源SqliteRetryingExecutionStrategy.cs 的 XML 文档解释得很清楚SQLite 在数据库层面串行化写入者即使在 WAL 模式下schema 修改语句也会短暂阻塞读者若不加重试并发的 EF Core 读取例如 OpenIddict 对umbracoOpenIddictTokens的令牌校验就会把这种瞬时锁暴露为SqliteException直接导致请求失败。重试预算约 56 秒该策略继承ExecutionStrategy的默认参数——DefaultMaxRetryCount6 次与DefaultMaxDelay30 秒产生约0s, 1s, 3s, 7s, 15s, 30s的指数退避延迟序列合计约 56 秒的重试窗口。在 EF Core 延迟之外还需要知道SQLITE_BUSY错误码 5会被Microsoft.Data.Sqlite在连接Default Timeout默认 30 秒内自行重试SQLITE_LOCKED错误码 6不会内部重试立即返回因此 EF Core 的重试预算是对它的唯一缓冲。判定逻辑ShouldRetryOn沿着异常内部链向上遍历凡是遇到IsBusyOrLocked()判定通过的SqliteException就返回true继续重试protected override bool ShouldRetryOn(Exception exception) { for (Exception? current exception; current is not null; current current.InnerException) { if (current is SqliteException sqlite sqlite.IsBusyOrLocked()) { return true; } } return false; }IsBusyOrLocked定义在父项目 SqliteExceptionExtensions.cs覆盖三种错误码SQLITE_BUSY、SQLITE_LOCKED、SQLITE_LOCKED_SHAREDCACHE。该扩展的注释还说明NPoco 栈Umbraco.Cms.Persistence.Sqlite有一份平行的同名实现两个栈相互独立、刻意重复以保持分层干净。测试验证集成测试 SqliteRetryingExecutionStrategyTests.cs 精确复现了 issue #22939 的场景基线测试用单独的 SQLite 连接对umbracoLock表持有一个事务性UPDATERESERVED 写锁超过命令超时测试把DefaultTimeout缩短到 1 秒不带重试策略的 DbContext 无论读SingleOrDefaultAsync还是写ExecuteUpdateAsync都会抛出SqliteException且错误码必须是SQLITE_BUSY或SQLITE_LOCKED容错测试同样持锁 2.5 秒但换成挂载了SqliteRetryingExecutionStrategy的 DbContext首个尝试失败后退避重试锁释放后操作成功无异常冒出测试用Assert.Ignore(SQLite-only scenario.)保证只在 SQLite 环境运行。四、迁移历史四条迁移逐一解读迁移总览迁移日期用途InitialCreate2023-06-22No-op——NPoco 创建基础表AddOpenIddict2023-08-07创建 OpenIddict 表Applications、Tokens、Authorizations、ScopesUpdateOpenIddictToV52024-04-03OpenIddict v5 的 schema 更新UpdateOpenIddictToV72025-10-06SQLite 下为 no-op无需 schema 变更InitialCreate为什么是 No-op20230622183638_InitialCreate.cs 的Up方法只有一行注释No op. Existing tables are added by npoco. This will only create the history table.这是理解 Umbraco 持久化架构的关键NPoco 负责创建全部基础表EF Core 只管理 OpenIddict 相关表。迁移本身只创建 EF Core 的迁移历史表__EFMigrationsHistory不触碰业务表。AddOpenIddict创建四张 OAuth 表20230807123456_AddOpenIddict.cs 创建全部以umbraco前缀命名的四张表表名用途umbracoOpenIddictApplicationsOAuth 客户端应用umbracoOpenIddictAuthorizations用户授权umbracoOpenIddictScopesOAuth 作用域umbracoOpenIddictTokens访问/刷新令牌SQLite 专属特征在这份迁移里体现得非常直观——所有字符串列一律使用TEXT类型SQLite 没有 varchar例如Id table.Columnstring(type: TEXT, nullable: false), ClientId table.Columnstring(type: TEXT, maxLength: 100, nullable: true), Subject table.Columnstring(type: TEXT, maxLength: 400, nullable: true),迁移还建立了外键关系Tokens → Applications、Tokens → Authorizations、Authorizations → Applications与多个索引其中包括IX_umbracoOpenIddictTokens_ReferenceId唯一等 OpenIddict 查询路径依赖的关键索引。UpdateOpenIddictToV5OpenIddict v5 的 schema 演进20240403141051_UpdateOpenIddictToV5.cs 对应 OpenIddict v5 的模型变化对umbracoOpenIddictApplications做了四处修改把Type列重命名为ClientType新增ApplicationType列TEXT, maxLength 50新增JsonWebKeySet列TEXT新增Settings列TEXT。Down方法逐一回滚这些改动。UpdateOpenIddictToV7SQLite 与 SQL Server 的差异样本20251006140958_UpdateOpenIddictToV7.cs 在 SQLite 下是空操作// No changes required for Sqlite而同名兄弟迁移 SqlServer 版 则把umbracoOpenIddictTokens.Type从nvarchar(50)扩宽为nvarchar(150)。原因在于 SQLite 的TEXT类型不强制长度约束无需 schema 变更即可容纳更长的类型值——这是同一份 OpenIddict 升级在不同数据库上有不同落法的典型范例也说明给每个提供程序单独维护迁移文件的必要性。五、新增迁移的完整操作流程向 SQLite 提供程序添加新迁移时文档与源码共同给出以下步骤配置连接字符串在src/Umbraco.Web.UI/appsettings.json中设置有效的 SQLite 连接字符串模板见 appsettings.template.jsonConnectionStrings:umbracoDbDSN确保安装器/启动时能按ConnectionStrings.ProviderName匹配到Microsoft.Data.Sqlite从仓库根目录运行迁移命令UmbracoDbContext的 XML 注释与父项目文档均给出标准命令# 新增迁移SQLite dotnet ef migrations add %MigrationName% -s src/Umbraco.Web.UI -p src/Umbraco.Cms.Persistence.EFCore.Sqlite -c UmbracoDbContext # 回滚最后一条迁移SQLite dotnet ef migrations remove -s src/Umbraco.Web.UI -p src/Umbraco.Cms.Persistence.EFCore.Sqlite # 生成迁移脚本 dotnet ef migrations script -s src/Umbraco.Web.UI -p src/Umbraco.Cms.Persistence.EFCore.SqlServer同步 SQL Server 提供程序因为两个提供程序共享同一个EFCoreMigration枚举与UmbracoDbContext模型新增迁移必须同时添加到src/Umbraco.Cms.Persistence.EFCore.SqlServer/Migrations/对应命令把-p换成 SqlServer 项目否则另一数据库无法完成同一次升级更新命名映射如果是新增命名迁移即可被MigrateAsync(EFCoreMigration)单独调用的迁移需要同时更新父项目枚举 EFCoreMigration.cs位于Umbraco.Infrastructure/Migrations更新 SqliteMigrationProvider.GetMigrationType() 的 switch 表达式对应的 SQL Server 提供程序做同样的映射更新。六、项目专属注意事项自动生成文件Migrations/UmbracoDbContextModelSnapshot.cs由 EF Core 工具自动生成并维护不要手动编辑。它记录当前模型状态用于生成增量迁移时做 diff 对比。提供程序名速查Constants.ProviderNames.SQLLite Microsoft.Data.Sqlite; // SQLite Constants.ProviderNames.SQLServer Microsoft.Data.SqlClient; // SQL Server相关项目父项目Umbraco.Cms.Persistence.EFCore定义IMigrationProvider/IMigrationProviderSetup接口、UmbracoDbContext基类、EFCoreMigrationExecutor编排器与分布式锁机制兄弟项目Umbraco.Cms.Persistence.EFCore.SqlServerSQL Server 对等实现迁移文件名不同如20251006140751_UpdateOpenIddictToV7schema 变更策略也因数据库类型而异NPoco 栈Umbraco.Cms.Persistence.Sqlite负责基础表创建与数据访问与 EF Core 栈并行共存。七、核心文件速查表文件用途SqliteMigrationProvider.cs命名迁移与全量迁移执行SqliteMigrationProviderSetup.csDbContext 配置UseSqlite 重试策略SqliteRetryingExecutionStrategy.cs对瞬时 BUSY/LOCKED 错误重试EFCoreSqliteComposer.csDI 注册Migrations/*.cs四条 SQLite 迁移文件SqliteRetryingExecutionStrategyTests.cs重试策略的集成测试UmbracoDbContext.cs基类表名前缀与提供程序配置分发小结Umbraco.Cms.Persistence.EFCore.Sqlite虽小却集中体现了 Umbraco 持久化层两个关键设计一是按数据库提供程序拆分实现通过父项目的接口抽象让迁移与配置完全可插拔二是用自定义重试策略补偿 SQLite 的锁语义把长迁移期间 OpenIddict 令牌校验的瞬时失败从硬错误变成可重试的等待。理解这三者Composer 注册 → Provider 迁移 → Retrying 容错的协作链路就能在 Umbraco 上放心使用 SQLite 作为开发或轻量部署数据库。赞分享CMS后端【免费下载链接】Umbraco-CMSUmbraco is a free and open source .NET content management system helping you deliver delightful digital experiences.项目地址https://gitcode.com/gh_mirrors/um/Umbraco-CMS点击查看免费下载相关推荐AI-on-the-edge-device 配置参数全览从 config.ini 到参数文档体系的完整指南AI on the edge device 配置参数全览从 config.ini 到参数文档体系的完整指南 导读 本文以 param docs/templatCMS后端Umbraco-CMS数据持久化策略NPoco与EF Core双轨并行的技术实现Umbraco CMS数据持久化策略NPoco与EF Core双轨并行的技术实现 Umbraco CMS作为一款流行的ASP.NET内容管理系统其数据持久化CMS后端Workflow Core 的 SQLite 持久化提供程序安装配置、底层原理与实战要点Workflow Core 的 SQLite 持久化提供程序安装配置、底层原理与实战要点 本指南围绕开源 .NET 工作流引擎 Workflow Core 的后端工作流自动化流程编排上一篇react-admin 数据请求完全指南从 useDataProvider 到 Query/Mutation Hooks 与乐观更新下一篇Neovide 命令行参数完整参考窗口、渲染、进程与 macOS 专属选项实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考 SEO 优化官网定制响应式建站教育培训建站