Hendra Anggrian Hendra Anggrian - 2 months ago 23
C# Question

Child collection not updating in EntityFrameworkCore

Consider the following simplified models with nested one-too-many relationship:

public class Report
{
public Guid ReportId { get; set; }
public string Name { get; set; }
public List<Schedule> Schedules { get; set; }
}

public class Schedule
{
public Guid ScheduleId { get; set; }
public string Title { get; set; }
public List<Description> Descriptions { get; set;}

// FK
public Guid ReportId { get; set; }
public Report Report { get; set; }
}

public class Description
{
public Guid DescriptionId { get; set; }
public string Content { get; set; }

// FK
public Guid ScheduleId { get; set; }
public Schedule Schedule { get; set; }
}

public class DataContext : DbContext
{
public DbSet<Report> Reports { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Filename=mydbname.db");
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Report>()
.HasMany(report => report.Schedules)
.WithOne(schedule => schedule.Report);
modelBuilder.Entity<Schedule>()
.HasOne(schedule => schedule.Report)
.WithMany(report => report.Schedules)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<Description>()
.HasOne(description => description.Schedule)
.WithMany(schedule => schedule.Descriptions)
.OnDelete(DeleteBehavior.Cascade);
}
}


Adding works as expected, each item is inserted in each table:

Description description = new Description();
description.DescriptionId = Guid.NewGuid();
description.Content = "my content";

Schedule schedule = new Schedule();
schedule.ScheduleId = Guid.NewGuid();
schedule.Title = "my schedule";
schedule.Descriptions = new List<Description>();
schedule.Descriptions.Add(description);

Report report = new Report;
report.ReportId = Guid.NewGuid();
report.Name = "my report";
report.Schedules = new List<Schedule>();
report.Schedules.Add(schedule);

using(var db = new DataContext())
{
db.Reports.Add(report);
db.SaveChanges();
}


Deleting entity also works as expected, all items are deleted in each table:

using(var db = new DataContext())
{
db.Reports.Remove(report);
db.SaveChanges();
}


However, updating entity only works if I change the content of existing item, not when I adding new item on collection. For example:

// this works
report.Name = "updated report";
report.Schedules.ElementAt(0).Descriptions.ElementAt(0).Content = "updated content";
using(var db = new DataContext())
{
db.Reports.Update(report);
db.SaveChanges();
}

// this throws DbUpdateConcurrencyException because no rows are affected.
Description newDescription = new Description();
newDescription.DescriptionId = Guid.NewGuid();
newDescription.Content = "new content";

Schedule newSchedule = new Schedule();
newSchedule.ScheduleId = Guid.NewGuid();
newSchedule.Title = "new schedule";
newSchedule.Descriptions = new List<Description>();
newSchedule.Descriptions.Add(newDescription);

report.Schedules.Add(newSchedule);
using(var db = new DataContext())
{
db.Reports.Update(report);
db.SaveChanges();
}


How do I correctly add item to collection and update it afterwards? Is my table design even makes sense? This is how I fetch Report objects:

List<Report> Reports;
using (var db = new DataContext())
Reports = db.Reports
.Include(report => report.Schedules)
.ThenInclude(schedule => schedule.Descriptions)
.ToList()


EDIT

Just as Sunteen - MSFT post implies, there seems to be nothing wrong with my code. I found out what's causing the problem in my case, which is I am not supposed to deliberately generate each entity's primary key ID (
Guid.NewGuid()
). As it turns out, the ID is auto-generated and the issue is resolved. Thank you Sunteen - MSFT.

Answer

I cannot reproduce your issue. The only code I wrote is to get the report object which you didn't show in your code snippet. Please ensure the report object you're trying to update is existing in the table. I just got the first record from the Report table and then update, code as follows,

using (var db = new DataContext())
{
    List<Report> Reports;
    Reports = db.Reports.Include(report1 => report1.Schedules)
                        .ThenInclude(schedule => schedule.Descriptions)
                    .ToList();
    var report = Reports[0];

    Description newDescription = new Description();
    newDescription.DescriptionId = Guid.NewGuid();
    newDescription.Content = "new content";

    Schedule newSchedule = new Schedule();
    newSchedule.ScheduleId = Guid.NewGuid();
    newSchedule.Title = "new schedule";
    newSchedule.Descriptions = new List<Description>();
    newSchedule.Descriptions.Add(newDescription);
    report.Schedules.Add(newSchedule);
    db.Reports.Update(report);
    db.SaveChanges();
}

I created a demo with your code and it worked well, and I uploaded the demo to github, you can download for testing and compare with your project to find differences. If it cannot resolved by compare differences, you can try to reproduce your issue on this demo and let us help solve again.

Comments