Skip to content

Commit

Permalink
[release/9.0] Prevent owner entity from becoming optional (#35222)
Browse files Browse the repository at this point in the history
Fixes #35110
  • Loading branch information
AndriySvyryd authored Dec 2, 2024
1 parent fba8789 commit cc53f41
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public class ForeignKeyPropertyDiscoveryConvention :
IPropertyFieldChangedConvention,
IModelFinalizingConvention
{
private static readonly bool UseOldBehavior35110 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35110", out var enabled) && enabled;

/// <summary>
/// Creates a new instance of <see cref="ForeignKeyPropertyDiscoveryConvention" />.
/// </summary>
Expand Down Expand Up @@ -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;
}
}
}

Expand Down
20 changes: 10 additions & 10 deletions src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
/// </summary>
public class InternalEntityTypeBuilder : InternalTypeBaseBuilder, IConventionEntityTypeBuilder
{
private static readonly bool UseOldBehavior35110 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35110", out var enabled) && enabled;

/// <summary>
/// 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
Expand Down Expand Up @@ -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),
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>("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<string>("Id");
b1.Property<string>("account_id");
b1.HasKey("Id");
b1.HasIndex("account_id");
b1.ToTable("account_holder");
b1.WithOwner().HasForeignKey("account_id");
});
});
},
target =>
{
target.Entity<Account>(builder =>
{
builder.ToTable("account");
builder.HasKey("Id");
builder.OwnsMany(a => a.AccountHolders, navigationBuilder =>
{
navigationBuilder.ToTable("account_holder");
navigationBuilder.Property<string>("Id");
navigationBuilder.HasKey("Id");
navigationBuilder.Property<string>("account_id");
navigationBuilder.WithOwner().HasForeignKey("account_id");
});
});
},
Assert.Empty);

public class Account
{
public string Id { get; set; }
public IEnumerable<AccountHolder> AccountHolders { get; set; } = [];
}

public class AccountHolder
{
}

[ConditionalFact]
public void SeedData_with_guid_AK_and_multiple_owned_types()
=> Execute(
Expand Down

0 comments on commit cc53f41

Please sign in to comment.