我正在使用MVC4开发一个使用EF代码第一种方法的网站。
我在删除具有一对多关系的实体中的子项时遇到一些问题。
编辑以清除:在我的编辑视图中,我在父级的子集合中添加/删除/更新现有的childen,使用javascript完成添加/删除。当我在控制器方法中的post请求中收到更新的父级时,我想同步/更新数据库中的父实体和子实体。 在视图中更新时,父对象处于分离状态。因此,当我再次附加父级时,我希望它执行在分离状态期间完成的所有更新。
设置实体关系,以便在从父集合中删除子实体时,子实体也会从子表中删除(级联删除排序?),这在附加状态下有效。
但是,在附加父项并保存更改时,只会在数据库中添加/修改添加/更新的子项。但是,从父集合中删除的子节点不会在数据库中删除(我想要它们)。
如何解决?
实体是:
class Parent
{
public virtual ICollection<Child> Children { get; set; }
}
class Child
{
public string Text { get; set; }
}
这样可以从数据库中删除子项:
void RemoveChildFromCollection()
{
// get the first parent and remove the first child in collection
var context = new DatabaseContext();
var parent = context.Parents.First();
parent.Children.Remove(parent.Children.First());
context.SaveChanges();
}
ControllerMethod:这不起作用,删除的子项不会从子表中删除
public ActionResult Edit(Parent parent)
{
var context = new DatabaseContext();
context.Entry(parent).State = System.Data.EntityState.Modified;
context.SaveChanges();
return View();
}
模型构建器设置为在从父集合中删除子实体时从子表中删除子实体
// Use Identifying relation. Define complex key for ChildObject containing both Id and
ParentObjectId
modelBuilder.Entity<Child>()
.HasKey(c => new {c.ChildID, c.ParentID});
// Because defining such key will remove default convention for auto incremented Id you must redefine it manually
modelBuilder.Entity<Child>()
.Property(c => c.ChildID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// Set cascade delete
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithRequired()
.HasForeignKey(c => c.ParentID)
.WillCascadeOnDelete();
答案 0 :(得分:2)
当您删除父实体时,级联删除仅删除子实体,如您所述,而不是在您断开关系时。
您可以覆盖上下文中的SaveChanges()
来清理孤立的Child
实体,如下所示:
public override int SaveChanges()
{
Children
.Local
.Where(c => c.Parent == null)
.ToList()
.ForEach(child => Children.Remove(child));
return base.SaveChanges();
}
此blog post有更多关于处理孤立实体的信息。
答案 1 :(得分:1)
当您在视图中进行修改时,您的实体(父实体和子实体)处于分离状态。因此,EF无法跟踪这些变化。将对象图附加到上下文时 - 通过将父级的状态设置为Modified
- EF将此附加对象图作为当前状态,并且不知道在分离阶段发生的子项的删除在视图中。
要解决此问题,您必须从数据库中加载当前对象图(包括子项的父项),将其与视图中的对象图进行比较,然后将更改合并到已加载的图中。然后保存更改。可能有几种可能的变化:
Children
集合Children
集合您当前的代码 - 将父级状态设置为Modified
- 只会正确处理第一种情况,而不会处理其他三种情况。
要处理所有四种情况,您需要按照上述步骤进行操作。如何执行此操作的示例显示为here(请参阅该答案中的修改部分)。
编辑帖子操作中的代码如下所示:
public ActionResult Edit(Parent parent)
{
using (var context = new DatabaseContext())
{
var parentInDb = context.Parents
.Include(p => p.Children)
.Single(p => p.ParentId == parent.ParentId);
context.Entry(parentInDb).CurrentValues.SetValues(parent);
foreach (var childInDb in parentInDb.Children.ToList())
if (!parent.Children.Any(c =>
c.ChildId == childInDb.ChildId &&
c.ParentId == childInDb.ParentId)) // or == parent.ParentId
context.Children.Remove(childInDb);
// here
// parentInDb.Children.Remove(childInDb);
// should work too because you have an identifying relationship
foreach (var child in parent.Children)
{
var childInDb = parentInDb.Children.SingleOrDefault(c =>
c.ChildId == child.ChildId &&
c.ParentId == child.ParentId); // or == parent.ParentId
if (childInDb != null)
context.Entry(childInDb).CurrentValues.SetValues(child);
else
parentInDb.Children.Add(child);
}
context.SaveChanges();
return View();
}
}