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

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

我不太理解这句话:

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

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

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

代码行可以提炼为:

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

当前回答

我也遇到了同样的问题,但我知道它在其他情况下也能正常工作,所以我把问题简化为:

parent.OtherRelatedItems.Clear();  //this worked OK on SaveChanges() - items were being deleted from DB
parent.ProblematicItems.Clear();   // this was causing the mentioned exception on SaveChanges()

OtherRelatedItems有一个复合主键(parentId +一些本地列),工作正常 probleaticitems有自己的单列主键,而parentId只是一个FK。这导致了Clear()之后的异常。

我所要做的就是使ParentId成为复合PK的一部分,以表明没有父元素就不能存在子元素。我使用DB-first模型,添加PK并将parentId列标记为EntityKey(因此,我必须在DB和EF中更新它-不确定EF单独是否足够)。

仔细想想,这是一个非常优雅的区别,EF使用它来决定没有父对象的子对象是否“有意义”(在这种情况下,Clear()不会删除它们并抛出异常,除非你将ParentId设置为其他/特殊的对象),或者-就像最初的问题一样-我们期望项一旦从父对象中删除就会删除。

其他回答

我今天遇到了这个问题,想分享我的解决方案。在我的例子中,解决方案是在从数据库中获取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

我在几个小时前遇到过这个问题,并尝试了所有方法,但在我的情况下,解决方案与上面列出的不同。

如果你使用已经从数据库检索实体,并试图修改它的孩子的错误将发生,但如果你从数据库获得实体的新副本,不应该有任何问题。 不要用这个:

 public void CheckUsersCount(CompanyProduct companyProduct) 
 {
     companyProduct.Name = "Test";
 }

用这个:

 public void CheckUsersCount(Guid companyProductId)
 {
      CompanyProduct companyProduct = CompanyProductManager.Get(companyProductId);
      companyProduct.Name = "Test";
 }

如果你正在使用自动绘图器,面临以下问题是一个很好的解决方案,它为我工作

https://www.codeproject.com/Articles/576393/Solutionplusto-aplus-Theplusoperationplusfailed

因为问题是我们正在映射空导航属性,我们实际上不需要在实体上更新它们,因为它们在契约上没有改变,我们需要在映射定义上忽略它们:

ForMember(dest => dest.RefundType, opt => opt.Ignore())

所以我的代码是这样的:

Mapper.CreateMap<MyDataContract, MyEntity>
ForMember(dest => dest.NavigationProperty1, opt => opt.Ignore())
ForMember(dest => dest.NavigationProperty2, opt => opt.Ignore())
.IgnoreAllNonExisting();

当我要删除我的记录时,我也遇到了同样的问题,因为这个问题的解决方案是,当你要删除你的记录时,你在删除头/主记录之前丢失了一些东西,你必须写代码在头/主记录之前删除它的细节,我希望你的问题会得到解决。

我使用了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; }
}