Skip to content

Commit 9d84cf4

Browse files
authored
Revert behavior to throw when attempting to modify an unconstrained alternate key property (#32492)
Fixes #28961 Reverts #30213 for #32385 In #32385, an unconstrained alternate key was added to the model purely to make a non-identifying relationship artificially identifying. #30213 attempted to fix this by throwing that the key was being modified. However, this scenario is very similar to the case for a many-to-many join type, where the composite primary key is also not the end of any relationship, but forces the two many-to-one relationships to be identifying. I prepared a PR that would only throw if the key involved is alternate, but on reflection that doesn't seem like an appropriate distinction to make. So overall, I think we should just revert this change, which is what this PR does.
1 parent cfcdf56 commit 9d84cf4

File tree

3 files changed

+31
-44
lines changed

3 files changed

+31
-44
lines changed

src/EFCore/ChangeTracking/Internal/NavigationFixer.cs

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,39 +1414,28 @@ private void ConditionallyNullForeignKeyProperties(
14141414

14151415
if (foreignKey.IsRequired
14161416
&& hasOnlyKeyProperties
1417-
&& dependentEntry.EntityState != EntityState.Detached
1418-
&& dependentEntry.EntityState != EntityState.Deleted)
1417+
&& dependentEntry.EntityState != EntityState.Detached)
14191418
{
1420-
if (foreignKey.DeleteBehavior == DeleteBehavior.Cascade
1421-
|| foreignKey.DeleteBehavior == DeleteBehavior.ClientCascade
1422-
|| foreignKey.IsOwnership)
1419+
try
14231420
{
1424-
try
1425-
{
1426-
_inFixup = true;
1427-
switch (dependentEntry.EntityState)
1428-
{
1429-
case EntityState.Added:
1430-
dependentEntry.SetEntityState(EntityState.Detached);
1431-
DeleteFixup(dependentEntry);
1432-
break;
1433-
case EntityState.Unchanged:
1434-
case EntityState.Modified:
1435-
dependentEntry.SetEntityState(
1436-
dependentEntry.SharedIdentityEntry != null ? EntityState.Detached : EntityState.Deleted);
1437-
DeleteFixup(dependentEntry);
1438-
break;
1439-
}
1440-
}
1441-
finally
1421+
_inFixup = true;
1422+
switch (dependentEntry.EntityState)
14421423
{
1443-
_inFixup = false;
1424+
case EntityState.Added:
1425+
dependentEntry.SetEntityState(EntityState.Detached);
1426+
DeleteFixup(dependentEntry);
1427+
break;
1428+
case EntityState.Unchanged:
1429+
case EntityState.Modified:
1430+
dependentEntry.SetEntityState(
1431+
dependentEntry.SharedIdentityEntry != null ? EntityState.Detached : EntityState.Deleted);
1432+
DeleteFixup(dependentEntry);
1433+
break;
14441434
}
14451435
}
1446-
else
1436+
finally
14471437
{
1448-
throw new InvalidOperationException(
1449-
CoreStrings.KeyReadOnly(dependentProperties.First().Name, dependentEntry.EntityType.DisplayName()));
1438+
_inFixup = false;
14501439
}
14511440
}
14521441
}

test/EFCore.Specification.Tests/GraphUpdates/GraphUpdatesTestBaseMiscellaneous.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2082,7 +2082,7 @@ private static SecondLaw AddSecondLevel(bool thirdLevel1, bool thirdLevel2)
20822082
return secondLevel;
20832083
}
20842084

2085-
[ConditionalTheory] // Issue #28961
2085+
[ConditionalTheory] // Issue #28961 and Issue #32385
20862086
[InlineData(false)]
20872087
[InlineData(true)]
20882088
public virtual Task Alternate_key_over_foreign_key_doesnt_bypass_delete_behavior(bool async)
@@ -2096,16 +2096,14 @@ public virtual Task Alternate_key_over_foreign_key_doesnt_bypass_delete_behavior
20962096
? await context.SaveChangesAsync()
20972097
: context.SaveChanges();
20982098

2099-
Assert.Equal(
2100-
CoreStrings.KeyReadOnly(nameof(SneakyChild.ParentId), nameof(SneakyChild)),
2101-
(await Assert.ThrowsAsync<InvalidOperationException>(
2102-
async () =>
2103-
{
2104-
parent.Children.Remove(parent.Children.First());
2105-
_ = async
2106-
? await context.SaveChangesAsync()
2107-
: context.SaveChanges();
2108-
})).Message);
2099+
Assert.Equal(2, context.ChangeTracker.Entries().Count());
2100+
2101+
parent.Children.Remove(parent.Children.First());
2102+
_ = async
2103+
? await context.SaveChangesAsync()
2104+
: context.SaveChanges();
2105+
2106+
Assert.Equal(1, context.ChangeTracker.Entries().Count());
21092107
});
21102108

21112109
[ConditionalTheory] // Issue #30764

test/EFCore.Specification.Tests/GraphUpdates/GraphUpdatesTestBaseOneToOne.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -856,9 +856,7 @@ public virtual void Save_required_one_to_one_changed_by_reference(
856856

857857
if (Fixture.ForceClientNoAction)
858858
{
859-
Assert.Equal(
860-
CoreStrings.KeyReadOnly(nameof(RequiredSingle1.Id), nameof(RequiredSingle1)),
861-
Assert.Throws<InvalidOperationException>(() => context.SaveChanges()).Message);
859+
Assert.Throws<DbUpdateException>(() => context.SaveChanges());
862860
}
863861
else
864862
{
@@ -1202,11 +1200,13 @@ public virtual void Sever_required_one_to_one(
12021200
throw new ArgumentOutOfRangeException(nameof(changeMechanism));
12031201
}
12041202

1203+
Assert.False(context.Entry(root).Reference(e => e.RequiredSingle).IsLoaded);
1204+
Assert.False(context.Entry(old1).Reference(e => e.Root).IsLoaded);
1205+
Assert.True(context.ChangeTracker.HasChanges());
1206+
12051207
if (Fixture.ForceClientNoAction)
12061208
{
1207-
Assert.Equal(
1208-
CoreStrings.KeyReadOnly(nameof(RequiredSingle1.Id), nameof(RequiredSingle1)),
1209-
Assert.Throws<InvalidOperationException>(() => context.SaveChanges()).Message);
1209+
Assert.Throws<DbUpdateException>(() => context.SaveChanges());
12101210
}
12111211
else
12121212
{

0 commit comments

Comments
 (0)