Allow excluding foreign key from migrations#37815
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a relational model annotation and fluent API to allow foreign key constraints to exist in the EF model while being excluded from migrations (i.e., migrations won’t create/drop the DB constraint), closing #15854.
Changes:
- Add
ExcludeFromMigrations()on relationship builders and persist it via a new relational FK annotation. - Update migrations diffing to skip add/drop FK operations when a foreign key constraint is excluded.
- Add design-time and functional test coverage (snapshot/codegen + provider SQL baselines) for the new behavior.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/EFCore.Sqlite.FunctionalTests/Migrations/MigrationsSqliteTest.cs | Adds provider SQL assertion for excluded-FK migration (index only). |
| test/EFCore.SqlServer.FunctionalTests/Migrations/MigrationsSqlServerTest.cs | Adds provider SQL assertion for excluded-FK migration (index only). |
| test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs | Adds model differ tests validating operations when FK constraints are excluded. |
| test/EFCore.Relational.Tests/Extensions/RelationalMetadataExtensionsTest.cs | Verifies get/set behavior for the new excluded-from-migrations FK metadata. |
| test/EFCore.Relational.Tests/Extensions/RelationalBuilderExtensionsTest.cs | Verifies fluent API surface and configuration-source behavior for FK exclusion. |
| test/EFCore.Relational.Tests/Design/AnnotationCodeGeneratorTest.cs | Ensures snapshot codegen emits ExcludeFromMigrations fluent API for FKs. |
| test/EFCore.Relational.Specification.Tests/Migrations/MigrationsTestBase.cs | Adds cross-provider scenario ensuring DB FK constraints aren’t created. |
| test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs | Updates “new annotations” guard lists for the new relational annotation name. |
| test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs | Adds snapshot roundtrip test verifying FK exclusion is stored in snapshots. |
| src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs | Skips FK add/drop operations when FK constraints are excluded from migrations. |
| src/EFCore.Relational/Metadata/RelationalAnnotationNames.cs | Introduces IsForeignKeyExcludedFromMigrations annotation name and registers it. |
| src/EFCore.Relational/Metadata/Internal/ForeignKeyConstraint.cs | Surfaces FK exclusion at the constraint level for migrations diffing. |
| src/EFCore.Relational/Metadata/IForeignKeyConstraint.cs | Adds IsExcludedFromMigrations to the public constraint metadata interface. |
| src/EFCore.Relational/Metadata/Conventions/RelationalRuntimeModelConvention.cs | Removes the new annotation from runtime annotations. |
| src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs | Adds metadata accessors for FK exclusion + configuration source helpers. |
| src/EFCore.Relational/Extensions/RelationalForeignKeyBuilderExtensions.cs | Adds ExcludeFromMigrations fluent APIs for relationship builders. |
| src/EFCore.Relational/Design/Internal/RelationalCSharpRuntimeAnnotationCodeGenerator.cs | Removes the new annotation from runtime codegen output. |
| src/EFCore.Relational/Design/AnnotationCodeGenerator.cs | Generates fluent API calls for FK exclusion in snapshots. |
| /// <param name="referenceCollectionBuilder">The builder being used to configure the relationship.</param> | ||
| /// <param name="excluded">A value indicating whether the foreign key constraint is excluded from migrations.</param> | ||
| /// <returns>The same builder instance so that multiple calls can be chained.</returns> | ||
| public static ReferenceCollectionBuilder ExcludeFromMigrations( |
There was a problem hiding this comment.
@AndriySvyryd see suggestion to rename this to ExcludeForeignKeyFromMigrations, to make it clear what's being excluded.
| if (runtime) | ||
| { | ||
| annotations.Remove(RelationalAnnotationNames.ForeignKeyMappings); | ||
| annotations.Remove(RelationalAnnotationNames.IsForeignKeyExcludedFromMigrations); |
There was a problem hiding this comment.
This is not a runtime annotation.
Modify a test in CompiledModelRelationalTestBase to verify this
| Assert.Equal("IX_Amoeba_ParentId", createIndexOperation.Name); | ||
| Assert.Equal(new[] { "ParentId" }, createIndexOperation.Columns); | ||
| }); | ||
|
|
There was a problem hiding this comment.
Also add a test that excludes an existing FK without removing or changing it and a test that excludes an existing FK and changes OnDelete on it
| Assert.Equal("LemonSupreme", foreignKey.GetConstraintName()); | ||
| } | ||
|
|
||
| [ConditionalFact] |
There was a problem hiding this comment.
Move these tests to RelationalModelBuilderTest, add tests for owned and many-to-many
Closes #15854