Search code examples
entity-frameworkasp.net-coreautomapper

Updating child/nested entities in Entity Framework


Trying to learn EF here. I have a very basic Parent Child one to many relationship and I am trying to see how I can update the Parent as well as Child entities in one go.

I have tried lots of different things but to avail. Any help would be greatly appreciated. Thank you.

Parent Class and Dto

public partial class Parent
{
    public Guid Id { get; set; }

    public string Name { get; set; }

    public virtual ICollection<Child> Children { get; set; } = new List<Child>();
}

Child Class and Dto

public partial class Child
{
    public Guid Id { get; set; }

    public Guid ParentId { get; set; }

    public virtual Parent Parent { get; set; }  <-- this is removed in the Child Dto

    public string Name { get; set; }
}

Payload

{
    "id": "bcf2678e-a318-4cc7-a8d0-1c1d887d4f8c",
    "name": " **New Value here** ",
    "children": [
        {
            "id": "1750c5a7-0845-49b8-9cae-064d5a03ef5f",
            "parentId": "bcf2678e-a318-4cc7-a8d0-1c1d887d4f8c",
            "name": " **New Value here** "
        },
        {
            "id": "77ffdbc9-3bc3-4569-9703-4b08187145dc",
            "parentId": "bcf2678e-a318-4cc7-a8d0-1c1d887d4f8c",
            "name": " **New Value here** "
        }
    ]
}

Error

{
    "statusCode": 500,
    "message": "The association between entity types 'Parent' and 'Child' 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. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the key values.",
}

Controller

[HttpPost("SetParent")]
public async Task<ActionResult<ParentDto>> SetParent(ParentDto parentDto)
{
    var parent = await _context.Parents
        .Where(f => f.Id == parentDto.Id)
        .Include(f => f.Children)
        .SingleOrDefaultAsync();

    if (parent == null) return NotFound();

    _mapper.Map(parentDto, parent);

    await _context.SaveChangesAsync();
    
    return Ok(parentDto);
}

Solution

  • Thanks for @XinranShen, the way to fix this issue is to change the OnDelete behavior to Cascade like the Error is saying.

    So changed the DeleteBehavior from

    modelBuilder.Entity<Child>(entity =>
            {
                entity.ToTable("Child", "Tools");
                entity.HasOne(d => d.Parent).WithMany(p => p.Children)
                     .HasForeignKey(d => d.ParentId)
                     .OnDelete(DeleteBehavior.ClientSetNull);
            });
    

    to

    modelBuilder.Entity<Child>(entity =>
            {
                entity.ToTable("Child", "Tools");
                entity.HasOne(d => d.Parent).WithMany(p => p.Children)
                     .HasForeignKey(d => d.ParentId)
                     .OnDelete(DeleteBehavior.Cascade);
            });