我得到这个错误时,我GetById()在一个实体,然后设置子实体的集合到我的新列表,来自MVC视图。

操作失败 关系是无法改变的 因为一个或多个外键 Properties是非空的。当一个 关系发生了变化 相关外键属性设置为 空值。如果外键是 不支持空值,新建 关系必须被定义 必须分配外键属性 另一个非空值或 必须删除不相关的对象。

我不太理解这句话:

这种关系无法改变 因为一个或多个外键 Properties是非空的。

我为什么要改变两个实体之间的关系?它应该在整个应用程序的生命周期内保持不变。

发生异常的代码只是简单地将集合中修改过的子类分配给现有的父类。这将有望满足取消子类,增加新的和修改。我本以为实体框架处理这个。

代码行可以提炼为:

var thisParent = _repo.GetById(1);
thisParent.ChildItems = modifiedParent.ChildItems();
_repo.Save();

当前回答

我发现这个答案对同样的错误更有帮助。 EF似乎不喜欢它当你删除,它更喜欢删除。

您可以像这样删除附加到记录上的记录集合。

order.OrderDetails.ToList().ForEach(s => db.Entry(s).State = EntityState.Deleted);

在本例中,所有附加到订单的Detail记录的状态都设置为Delete。(准备添加回更新的详细信息,作为订单更新的一部分)

其他回答

我使用了Mosh的解决方案,但我不清楚如何在代码中正确地实现组合键。

这就是解决方案:

public class Holiday
{
    [Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int HolidayId { get; set; }
    [Key, Column(Order = 1), ForeignKey("Location")]
    public LocationEnum LocationId { get; set; }

    public virtual Location Location { get; set; }

    public DateTime Date { get; set; }
    public string Name { get; set; }
}

这种解决方案对我来说很管用:

Parent original = db.Parent.SingleOrDefault<Parent>(t => t.ID == updated.ID);
db.Childs.RemoveRange(original.Childs);
updated.Childs.ToList().ForEach(c => original.Childs.Add(c));
db.Entry<Parent>(original).CurrentValues.SetValues(updated);

重要的是,这将删除所有记录并重新插入它们。 但对于我的情况(少于10),这是可以的。

我希望这能有所帮助。

我今天遇到了这个问题,想分享我的解决方案。在我的例子中,解决方案是在从数据库中获取Parent之前删除Child项。

以前我是这样做的代码如下。然后我将得到这个问题中列出的相同错误。

var Parent = GetParent(parentId);
var children = Parent.Children;
foreach (var c in children )
{
     Context.Children.Remove(c);
}
Context.SaveChanges();

对我来说,有效的方法是先获取子项,使用parentId(外键),然后删除这些项。然后我可以从数据库中获取父元素,在这一点上,它应该不再有任何子元素,我可以添加新的子元素。

var children = GetChildren(parentId);
foreach (var c in children )
{
     Context.Children.Remove(c);
}
Context.SaveChanges();

var Parent = GetParent(parentId);
Parent.Children = //assign new entities/items here

出现这个问题是因为我们试图删除父表,但仍然存在子表数据。 我们利用级联删除来解决这个问题。

在模型中创建dbcontext类中的方法。

 modelBuilder.Entity<Job>()
                .HasMany<JobSportsMapping>(C => C.JobSportsMappings)
                .WithRequired(C => C.Job)
                .HasForeignKey(C => C.JobId).WillCascadeOnDelete(true);
            modelBuilder.Entity<Sport>()
                .HasMany<JobSportsMapping>(C => C.JobSportsMappings)
                  .WithRequired(C => C.Sport)
                  .HasForeignKey(C => C.SportId).WillCascadeOnDelete(true);

之后,在我们的API调用中

var JobList = Context.Job                       
          .Include(x => x.JobSportsMappings)                                     .ToList();
Context.Job.RemoveRange(JobList);
Context.SaveChanges();

级联删除选项删除父表以及与父相关的子表,使用这个简单的代码。用这个简单的方法试试。

删除范围用于删除数据库中的记录列表 谢谢

这是一个很大的问题。在你的代码中实际发生的是这样的:

从数据库加载Parent并获得附加实体 您用分离的子集合替换它的子集合 您保存更改,但在此操作期间,所有的子元素都被认为是添加的,因为EF直到此时才知道它们。因此EF尝试将null设置为旧子节点的外键,并插入所有新的子节点=>重复行。

解决方法取决于你想做什么以及你想怎么做?

如果您正在使用ASP。NET MVC你可以尝试使用UpdateModel或TryUpdateModel。

如果你只是想手动更新现有的子程序,你可以简单地这样做:

foreach (var child in modifiedParent.ChildItems)
{
    context.Childs.Attach(child); 
    context.Entry(child).State = EntityState.Modified;
}

context.SaveChanges();

附加实际上是不需要的(将状态设置为Modified也将附加实体),但我喜欢它,因为它使过程更加明显。

如果你想修改现有的,删除现有的和插入新的子元素,你必须这样做:

var parent = context.Parents.GetById(1); // Make sure that childs are loaded as well
foreach(var child in modifiedParent.ChildItems)
{
    var attachedChild = FindChild(parent, child.Id);
    if (attachedChild != null)
    {
        // Existing child - apply new values
        context.Entry(attachedChild).CurrentValues.SetValues(child);
    }
    else
    {
        // New child
        // Don't insert original object. It will attach whole detached graph
        parent.ChildItems.Add(child.Clone());
    }
}

// Now you must delete all entities present in parent.ChildItems but missing
// in modifiedParent.ChildItems
// ToList should make copy of the collection because we can't modify collection
// iterated by foreach
foreach(var child in parent.ChildItems.ToList())
{
    var detachedChild = FindChild(modifiedParent, child.Id);
    if (detachedChild == null)
    {
        parent.ChildItems.Remove(child);
        context.Childs.Remove(child); 
    }
}

context.SaveChanges();