dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.75k stars 3.18k forks source link

When a parent and child entity has a one to many relationship and the child table is a temporal table, removing a child from its parent collection will trigger an unexpected error. #31535

Closed RushuiGuan closed 1 year ago

RushuiGuan commented 1 year ago

To get this error, three conditions are required

When the parent has an existing child entity, removing the child entity from its parent collection should have an expected operation of deleting the child entity because its relationship to a parent is required. Efcore handles this correctly if the child table is not a temporal table. If it is, the following error will be thrown: System.InvalidOperationException: The association between entities 'Parent' and 'Child' with the key value '{ParentId: 7}' has been severed, but the relationship is either marked as required or is implicitly required because the foreign key is not nullable. If the dependent/child entity should be deleted when a required relationship is severed, configure the relationship to use cascade deletes. To get pass this error, set the DeleteBehavior to Cascade or ClientDelete. EfCore then behaves as expected.

Here is the code sample

using Microsoft.EntityFrameworkCore;

namespace SampleCode
{
    public class Parent
    {
        public int Id { get; set; }
        public List<Child> Children { get; set; } = new List<Child>();
    }

    public class Child
    {
        public int Id { get; set; }
        public Parent Parent { get; set; } = null!;
        public int ParentId { get; set; }
    }

    public class MyDbContext : DbContext
    {
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            var parentBuilder = modelBuilder.Entity<Parent>();
            parentBuilder.HasKey(p => p.Id);
            parentBuilder.HasMany(p => p.Children).WithOne(p => p.Parent)
                .HasForeignKey(p => p.ParentId).OnDelete(DeleteBehavior.Restrict);

            var childBuilder = modelBuilder.Entity<Child>();
            childBuilder.ToTable(nameof(Child), b => b.IsTemporal());
            childBuilder.HasKey(p => p.Id);
        }
    }
}

EF Core version: 7.0.9 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: NET 7.0 Operating system: Windows 11 IDE: Visual Studio 2022 17.4

Please also see the attached working sample code.

Thanks

Rushui

SampleCode.zip

ajcvickers commented 1 year ago

@RushuiGuan This is by-design.

When the parent has an existing child entity, removing the child entity from its parent collection should have an expected operation of deleting the child entity because its relationship to a parent is required

This is what the DeleteBehavior configures. By configuring it as Restrict, you are telling EF not to do this. But see also #10066.

RushuiGuan commented 1 year ago

Hi Arthur

Thanks for the reply. I double checked the code and it seems that the current behavior of the efcore is still unchanged from #10066. I made the mistake of thinking that the default value of the DeleteBehavior is Restricted, but in reality it is actually Cascade.

But I so strongly agree with @ajbeaven's comment below that I just assumed the efcore would delete the child item when removed from the parent collection.

The OnDelete action specifies the action to take when ParentEntity is deleted. ParentEntity is not being deleted in the case above, so I question why the OnDelete action has any bearing as to what occurs here.

Anyway, thanks for the education. Hope the team can make this enhancement one day!

Best,

Rushui