diff --git a/src/EFCore/Metadata/Internal/ForeignKey.cs b/src/EFCore/Metadata/Internal/ForeignKey.cs index d0f5ad88e54..c328ec450c0 100644 --- a/src/EFCore/Metadata/Internal/ForeignKey.cs +++ b/src/EFCore/Metadata/Internal/ForeignKey.cs @@ -1210,6 +1210,13 @@ public static bool AreCompatible( if (!ArePropertyTypesCompatible(principalProperties, dependentProperties)) { + if (principalEntityType.Model is Model model + && ReferenceEquals(model, dependentEntityType.Model) + && model.IsInModelSnapshot) + { + return true; + } + if (shouldThrow) { throw new InvalidOperationException( diff --git a/src/EFCore/Metadata/Internal/Model.cs b/src/EFCore/Metadata/Internal/Model.cs index dab7ec6d556..a1de99b8fcb 100644 --- a/src/EFCore/Metadata/Internal/Model.cs +++ b/src/EFCore/Metadata/Internal/Model.cs @@ -102,6 +102,20 @@ public virtual ModelDependencies? ScopedModelDependencies set => _scopedModelDependencies = value; } + /// + /// Gets a value indicating whether this model originated from a migration snapshot. + /// + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual bool IsInModelSnapshot + => ScopedModelDependencies == null + && _modelFinalizedConventions is { Count: 0 } + && FindAnnotation(CoreAnnotationNames.ProductVersion) != null; + /// /// Indicates whether the model is read-only. /// diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs index ccc47ab78c3..01a721bde3b 100644 --- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs +++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs @@ -121,6 +121,54 @@ protected override void BuildModel(ModelBuilder modelBuilder) Assert.Equal(2, snapshot.Model.GetEntityTypes().Count()); } + [Fact] + public void Snapshot_with_mismatched_key_and_foreign_key_property_types_is_usable() + { + const string snapshotCode = + """ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; + +#nullable disable + +namespace RootNamespace; + +partial class Snapshot : ModelSnapshot +{ + protected override void BuildModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "10.0.0"); + + modelBuilder.Entity("Dependent", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasOne("Principal") + .WithMany() + .HasForeignKey("Id"); + }); + + modelBuilder.Entity("Principal", b => + { + b.Property("Id") + .HasColumnType("smallint"); + + b.HasKey("Id"); + }); + } +} + +"""; + + var snapshotModel = BuildModelFromSnapshotSource(snapshotCode); + + Assert.Single(snapshotModel.FindEntityType("Dependent")!.GetForeignKeys()); + } + [Fact] public void Snapshot_with_migration_id() { diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs index 275a48afaea..eb8bf76b96c 100644 --- a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs +++ b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs @@ -592,6 +592,18 @@ public virtual void Warns_on_double_uniquified_shadow_key_due_to_wrong_type() modelBuilder); } + [Fact] + public virtual void Passes_on_foreign_key_with_matching_model_type_and_mismatched_provider_type() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder.Entity().HasOne().WithMany().HasForeignKey(a => a.P0).HasPrincipalKey(b => b.Id); + modelBuilder.Entity().Property(a => a.P0).HasConversion(); + modelBuilder.Entity().Property(b => b.Id).HasConversion(); + + Validate(modelBuilder); + } + [Fact] public virtual void Detects_shadow_key_referenced_by_foreign_key_by_convention() {