diff --git a/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs index 45194d0a2a1..beb97f2b1b1 100644 --- a/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs @@ -50,6 +50,9 @@ public class ForeignKeyPropertyDiscoveryConvention : IPropertyFieldChangedConvention, IModelFinalizingConvention { + private static readonly bool UseOldBehavior35110 = + AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35110", out var enabled) && enabled; + /// /// Creates a new instance of . /// @@ -81,13 +84,17 @@ private IConventionForeignKeyBuilder ProcessForeignKey( IConventionContext context) { var shouldBeRequired = true; - foreach (var property in relationshipBuilder.Metadata.Properties) + if (!relationshipBuilder.Metadata.IsOwnership + || UseOldBehavior35110) { - if (property.IsNullable) + foreach (var property in relationshipBuilder.Metadata.Properties) { - shouldBeRequired = false; - relationshipBuilder = relationshipBuilder.IsRequired(false) ?? relationshipBuilder; - break; + if (property.IsNullable) + { + shouldBeRequired = false; + relationshipBuilder = relationshipBuilder.IsRequired(false) ?? relationshipBuilder; + break; + } } } diff --git a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs index dfeeb09c785..53d96998377 100644 --- a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs @@ -15,6 +15,9 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal; /// public class InternalEntityTypeBuilder : InternalTypeBaseBuilder, IConventionEntityTypeBuilder { + private static readonly bool UseOldBehavior35110 = + AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35110", out var enabled) && enabled; + /// /// 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 @@ -3161,17 +3164,9 @@ public static InternalIndexBuilder DetachIndex(Index indexToDetach) && targetEntityType.Name == existingTargetType.ClrType.DisplayName()))) { relationship = existingNavigation.ForeignKey.Builder; - if (existingNavigation.ForeignKey.IsOwnership) - { - relationship = relationship.IsOwnership(true, configurationSource) - ?.HasNavigations(inverse, navigation, configurationSource); - - relationship?.Metadata.UpdateConfigurationSource(configurationSource); - return relationship; - } - Check.DebugAssert( - !existingTargetType.IsOwned() + existingNavigation.ForeignKey.IsOwnership + || !existingTargetType.IsOwned() || existingNavigation.DeclaringEntityType.IsInOwnershipPath(existingTargetType) || (existingTargetType.IsInOwnershipPath(existingNavigation.DeclaringEntityType) && existingTargetType.FindOwnership()!.PrincipalEntityType != existingNavigation.DeclaringEntityType), @@ -3181,6 +3176,11 @@ public static InternalIndexBuilder DetachIndex(Index indexToDetach) relationship = relationship.IsOwnership(true, configurationSource) ?.HasNavigations(inverse, navigation, configurationSource); + if (!UseOldBehavior35110) + { + relationship = relationship?.IsRequired(true, configurationSource); + } + relationship?.Metadata.UpdateConfigurationSource(configurationSource); return relationship; } diff --git a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs index 3c00a36a40b..5ca1a781feb 100644 --- a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs +++ b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs @@ -9763,6 +9763,62 @@ public void Update_AK_seed_value_with_a_referencing_foreign_key() v => Assert.Equal(4242, v)); })); + [ConditionalFact] + public void Owned_collection_with_explicit_id() + => Execute( + modelBuilder => + { + }, + source => + { + source.Entity("Microsoft.EntityFrameworkCore.Migrations.Internal.Account", b => + { + b.Property("Id"); + b.HasKey("Id"); + b.ToTable("account"); + }); + + source.Entity("Microsoft.EntityFrameworkCore.Migrations.Internal.Account", b => + { + b.OwnsMany("Microsoft.EntityFrameworkCore.Migrations.Internal.AccountHolder", "AccountHolders", b1 => + { + b1.Property("Id"); + b1.Property("account_id"); + b1.HasKey("Id"); + b1.HasIndex("account_id"); + b1.ToTable("account_holder"); + b1.WithOwner().HasForeignKey("account_id"); + }); + }); + }, + target => + { + target.Entity(builder => + { + builder.ToTable("account"); + builder.HasKey("Id"); + builder.OwnsMany(a => a.AccountHolders, navigationBuilder => + { + navigationBuilder.ToTable("account_holder"); + navigationBuilder.Property("Id"); + navigationBuilder.HasKey("Id"); + navigationBuilder.Property("account_id"); + navigationBuilder.WithOwner().HasForeignKey("account_id"); + }); + }); + }, + Assert.Empty); + + public class Account + { + public string Id { get; set; } + public IEnumerable AccountHolders { get; set; } = []; + } + + public class AccountHolder + { + } + [ConditionalFact] public void SeedData_with_guid_AK_and_multiple_owned_types() => Execute(