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 → IConvention*Builder。

構成可能なメタデータファセットの追加

公開インターフェイスだけで終わらせず、最も近い既存のファセットを端から端までたどってください。

  1. 読み取り専用、変更可能、規約用のメタデータコントラクト、具象ストレージ、既定値、構成ソース、規約セッター、ビルダーの優先順位チェックを追加します。メタデータの置き換え時やリレーションシップの再構築時にも、ファセットを保持してください。
  2. 変更が規約から観察できる場合は、規約インターフェイス、ConventionSet の追加/置換/削除のすべての経路、即時および遅延ディスパッチャーのスコープとノードを追加します。
  3. RuntimeModelConvention、実行時のコンストラクター/ビルダー、CSharpRuntimeModelCodeGenerator を通じて、最適化された実行時メタデータに値を引き継ぎ、コンパイル済みモデルでも保持されるようにします。
  4. スナップショットにファセットを保持する場合は、流暢なビルダー 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. フィルターで除外された場合は、ランタイムモデル上でアクセスされたときに例外をスローするロジックをゲッターに追加します
  3. 実行時に遅延計算できる場合(例:他のアノテーションに基づく場合)は、CSharpRuntimeAnnotationCodeGenerator.Generate でフィルターします
  4. アップマイグレーションまたはリレーショナルモデルで使用する場合は RelationalAnnotationProvider に、ダウンマイグレーションで使用する場合は IMigrationsAnnotationProvider に伝播させます

リレーショナルモデル

RelationalModel(src/EFCore.Relational/Metadata/Internal/RelationalModel.cs)は、エンティティ型を物理データベースオブジェクト(Tables、Views、Functions、Queries、DefaultTables)にマッピングする、EF モデルのデータベース中心のビューです。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 呼び出しの間で実行されます。

マイグレーションスナップショットとの互換性

モデル構築の変更によって、アップグレードしたユーザーに不要なマイグレーションが発生することがあります。原因は次の 2 つです。

  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() が、例外をスローせず簡潔な内容を表示する
  • 既存のスナップショットを持つプロジェクトに対して不要なマイグレーションが生成されない

コメント