子集合在EntityFrameworkCore中没有更新
本文关键字:更新 EntityFrameworkCore 子集合 | 更新日期: 2023-09-27 18:05:34
考虑以下使用嵌套一对多关系的简化模型:
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);
}
}
按预期工作,每个条目都插入到每个表中:
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();
}
删除实体也像预期的那样工作,在每个表中删除所有项:
using(var db = new DataContext())
{
db.Reports.Remove(report);
db.SaveChanges();
}
但是,更新实体只在更改现有项的内容时有效,而在集合上添加新项时无效。例如:
// 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();
}
如何正确地将项目添加到集合并在之后更新它?我的表格设计有意义吗?这是我如何获取报告对象:
List<Report> Reports;
using (var db = new DataContext())
Reports = db.Reports
.Include(report => report.Schedules)
.ThenInclude(schedule => schedule.Descriptions)
.ToList()
编辑
正如Sunteen - MSFT帖子所暗示的那样,我的代码似乎没有任何问题。我发现了在我的情况下导致问题的原因,即我不应该故意生成每个实体的主键ID (Guid.NewGuid()
)。事实证明,ID是自动生成的,问题得到了解决。谢谢Sunteen - MSFT.
我无法重现您的问题。我写的唯一代码是获得report
对象,您没有在代码片段中显示。请确保您试图更新的report
对象存在于表中。我刚刚从Report
表中获得了第一条记录,然后更新,代码如下,
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();
}
我用你的代码做了一个demo,效果很好,我把demo上传到github上,你可以下载测试,和你的项目比较,发现差异。如果通过比较差异无法解决,您可以尝试在此演示中重现您的问题,并让我们再次帮助解决。
我知道这是一个老问题,但是(正如建议的答案所建议的),您应该使用相同的DbContext
来查询,修改/创建,然后保存更改。
当您查询并获得一个实体,然后处置上下文时,您查询的实体现在断开连接,因为您已经丢失了作为原始DbContext
一部分的更改跟踪器。如果您修改了那个断开连接的实体,然后创建了一个新的DbContext
,您需要在保存更改之前将其重新附加到新的上下文中,否则您将遇到您所遇到的错误。
EF(特别是EF Core)足够聪明,如果你调用DbSet<T>.Update(T entity)
(例如db.Reports.Update(report)
),它可以递归地发现导航属性的变化,并将其标记为跟踪。
故事寓意:
对所有相关的DB操作使用单个DbContext
,从检索现有实体的查询开始(如果您正在进行更新),或者在添加新记录时调用DbSet<T>.Add(T entity)
。然后在处理上下文之前调用DbContext.SaveChanges()
。