mcpField
M
model-building

model-building

github.com/dotnet开发14.8k54 次安装今天更新
代码仓库

关于 model-building

model-building 是 Entity Framework Core 约定、元数据、初始化和关系模型的开发参考技能,涵盖端到端变更、兼容性问题及针对性测试验证。

主要功能

  1. 01约定系统与元数据层级
  2. 02可配置元数据属性的实现
  3. 03模型生命周期与初始化
  4. 04运行时及编译模型传递
  5. 05快照兼容性指引
  6. 06模型验证与测试位置

适用场景

  1. 01添加可配置的元数据属性
  2. 02调试约定与关系发现
  3. 03将注释传递到运行时模型
  4. 04避免模型变更后产生意外迁移

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dotnet/efcore --skill model-building

安装后在 Claude Code 中输入/model-building确认已加载。

详细介绍

模型构建、约定与初始化

涵盖模型构建(约定、流畅 API、元数据层级)和模型初始化(运行时注解传播、编译模型筛选)。

约定系统

ConventionSet(src/EFCore/Metadata/Conventions/ConventionSet.cs)为每种元数据事件保存一个 List<I*Convention>。src/EFCore/Metadata/Conventions/ 中的重要约定包括:

  • DbSetFindingConvention — 从 DbSet<T> 中发现实体
  • PropertyDiscoveryConvention — 从 CLR 类型中发现属性
  • KeyDiscoveryConvention — 查找主键(Id、TypeId)
  • RelationshipDiscoveryConvention — 根据导航推断外键
  • RuntimeModelConvention — 从可变模型创建优化后的 RuntimeModel

重写 ConfigureConventions(ModelConfigurationBuilder) 以添加或移除约定。

修复约定时,必须保留分派器行为和歧义信息。对于反向导航特性,不要通过永久忽略有歧义的导航来解决递归问题:移除一个候选项可能会让另一个歧义变得唯一,并配置错误的关系。InversePropertyAttributeConvention 会记录反向候选项,并使用 AddInverseNavigation、TryRemoveIfAmbiguous 和 FindAmbiguousInverse,同时继续进行约定处理。

测试状态转换,而不只是初始的有歧义模型:首先验证多个相互作用的候选项不会配置任何关系;然后分别通过忽略/移除一个竞争导航,以及移除一个竞争实体类型,真正消除歧义,并验证剩余的反向关系已配置。这些情况可以区分候选项跟踪与永久忽略的变通方案。

元数据接口层级

IReadOnly* → IMutable* → IConvention* → IRuntime*

适用于:Model、EntityType、Property、Key、ForeignKey、Navigation、Index 等。Builder 的层级为:*Builder → IConvention*Builder。

添加可配置的元数据属性

从头到尾参照最接近的现有属性实现,不要只修改公共接口:

  1. 添加只读、可变和约定元数据契约、具体存储、默认值、配置源、约定 setter 和 Builder 优先级检查。替换元数据或重建关系时,保留该属性。
  2. 如果约定可以观察到变更,则添加约定接口、ConventionSet 中所有添加/替换/移除路径,以及即时和延迟的分派器作用域与节点。
  3. 通过 RuntimeModelConvention、运行时构造函数/Builder 和 CSharpRuntimeModelCodeGenerator 将值传递到优化后的运行时元数据中,以确保编译模型保留该值。
  4. 如果快照会持久化该属性,则更新流畅 Builder API 和 CSharpSnapshotGenerator;旧快照中缺少该属性时,应等同于历史默认值。
  5. 更新公共 API 基线,并覆盖配置源优先级、约定分派、运行时/编译模型传播和快照往返测试。

模型生命周期

  1. 可变模型 — 在 OnModelCreating 期间由 ModelBuilder 构建,通过 FinalizeModel() 设为只读
  2. 设计时模型 — 已完成且只读的 Model,还包含迁移使用的仅设计时注解
  3. 运行时模型 — 由 RuntimeModelConvention.ProcessModelFinalized() 创建的优化只读模型,不包含仅设计时注解

ModelRuntimeInitializer.Initialize()(由 DbContextServices.CreateModel() 调用):

Initialize(model, designTime, validationLogger)
├─ FinalizeModel() if mutable
├─ Set ModelDependencies, InitializeModel
└─ RuntimeModelConvention creates RuntimeModel, copies/filters annotations

复杂类型属性递归

在约定或验证中处理属性时,请记住复杂类型可以包含其自身声明的属性。使用 GetFlattenedProperties() 遍历所有属性(包括嵌套的非集合复杂类型上的属性),或手动递归遍历 GetDeclaredComplexProperties() → complexProperty.ComplexType。

添加新注解

  1. 在 CoreAnnotationNames 及其 AllNames 中添加常量
  2. 如果这是仅用于设计时的注解(仅用于迁移操作),则在 RuntimeModelConvention.ProcessModelAnnotations 中进行筛选
    1. 如果该注解被筛除,则在 getter 中添加逻辑:如果在运行时模型上访问该注解,则抛出异常
  3. 如果该注解可在运行时延迟计算(例如,基于其他注解),则在 CSharpRuntimeAnnotationCodeGenerator.Generate 中进行筛选
  4. 如果该注解用于升级迁移或关系模型,则在 RelationalAnnotationProvider 中传递;如果用于降级迁移,则在 IMigrationsAnnotationProvider 中传递

关系模型

RelationalModel(src/EFCore.Relational/Metadata/Internal/RelationalModel.cs)是以数据库为中心的 EF 模型视图,将实体类型映射到物理数据库对象:Tables、Views、Functions、Queries 和 DefaultTables。DefaultTables 是仅用于 FromSql 查询的伪表对象。

由 RelationalModelRuntimeInitializer 延迟创建,通过 model.GetRelationalModel() 访问。用于迁移(MigrationsModelDiffer)、更新和查询管道。

RelationalAnnotationProvider 会填充关系模型元素上的注解。提供程序子类(例如 SqlServerAnnotationProvider)会添加提供程序特定的注解。IMigrationsAnnotationProvider 控制降级迁移操作中使用的注解。

模型验证

ModelValidator(src/EFCore/Infrastructure/ModelValidator.cs)和 RelationalModelValidator(src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs)会在模型最终确定后运行,即 ModelRuntimeInitializer.Initialize() 中的前置和后置验证 InitializeModel 调用之间。

迁移快照兼容性

模型构建变更可能会导致升级的用户遇到虚假迁移。原因有两个:

  1. 快照中写入了新的元数据 — 旧快照中没有该元数据;MigrationsModelDiffer 会检测到差异。修复方法:确保将旧快照中缺少该注解的情况视为旧默认值。
  2. 注解已重命名或重新解释 — 旧快照会生成不同的模型。修复方法:保留向后兼容的读取逻辑。

检查 CSharpSnapshotGenerator(写入的内容)和 MigrationsModelDiffer(如何处理缺失情况)。在 test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs 中添加快照往返测试。

测试

区域位置
约定单元测试test/EFCore.Tests/Metadata/Conventions/
元数据单元测试test/EFCore.Tests/Metadata/Internal/
模型构建器 API 测试test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest*.cs
关系发现测试test/EFCore.Specification.Tests/ModelBuilding101*.cs
模型验证测试test/EFCore.Tests/Infrastructure/ModelValidatorTest*.cs
已编译模型测试test/EFCore.Specification.Tests/Scaffolding/CompiledModelTestBase.cs

验证

  • 模型构建期间,在最终确定时不会出现 InvalidOperationException
  • 所有新增 API 均有测试覆盖
  • 使用 EF_TEST_REWRITE_BASELINES=1 可顺利更新已编译模型基线
  • 元数据对象上的 ToString() 能简洁地显示内容,且不会抛出异常
  • 针对已有快照的项目不会生成虚假迁移

评论

支持 Markdown