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

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

我不太理解这句话:

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

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

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

代码行可以提炼为:

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

当前回答

我只是犯了同样的错误。 我有两个具有父子关系的表,但是我在子表的表定义中的外键列上配置了“on delete cascade”。 因此,当我手动删除父行(通过SQL)在数据库中,它将自动删除子行。

然而,这在EF中不起作用,出现了这个线程中描述的错误。 原因是,在我的实体数据模型(edmx文件)中,父表和子表之间的关联属性不正确。 End1的OnDelete选项被配置为none(“End1”在我的模型中是具有1的多重性的结束)。

我手动将End1 OnDelete选项改为Cascade,然后它就工作了。 我不知道为什么EF不能拾取这个,当我从数据库更新模型(我有一个数据库第一模型)。

为了完整起见,这是我删除代码的样子:

   public void Delete(int id)
    {
        MyType myObject = _context.MyTypes.Find(id);

        _context.MyTypes.Remove(myObject);
        _context.SaveChanges(); 
   }    

如果我没有定义级联删除,我将不得不在删除父行之前手动删除子行。

其他回答

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

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

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

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

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

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),这是可以的。

我希望这能有所帮助。

你必须手动清除ChildItems集合,并在其中添加新项目:

thisParent.ChildItems.Clear();
thisParent.ChildItems.AddRange(modifiedParent.ChildItems);

之后,您可以调用DeleteOrphans扩展方法,它将处理孤立的实体(它必须在DetectChanges和SaveChanges方法之间调用)。

public static class DbContextExtensions
{
    private static readonly ConcurrentDictionary< EntityType, ReadOnlyDictionary< string, NavigationProperty>> s_navPropMappings = new ConcurrentDictionary< EntityType, ReadOnlyDictionary< string, NavigationProperty>>();

    public static void DeleteOrphans( this DbContext source )
    {
        var context = ((IObjectContextAdapter)source).ObjectContext;
        foreach (var entry in context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
        {
            var entityType = entry.EntitySet.ElementType as EntityType;
            if (entityType == null)
                continue;

            var navPropMap = s_navPropMappings.GetOrAdd(entityType, CreateNavigationPropertyMap);
            var props = entry.GetModifiedProperties().ToArray();
            foreach (var prop in props)
            {
                NavigationProperty navProp;
                if (!navPropMap.TryGetValue(prop, out navProp))
                    continue;

                var related = entry.RelationshipManager.GetRelatedEnd(navProp.RelationshipType.FullName, navProp.ToEndMember.Name);
                var enumerator = related.GetEnumerator();
                if (enumerator.MoveNext() && enumerator.Current != null)
                    continue;

                entry.Delete();
                break;
            }
        }
    }

    private static ReadOnlyDictionary<string, NavigationProperty> CreateNavigationPropertyMap( EntityType type )
    {
        var result = type.NavigationProperties
            .Where(v => v.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
            .Where(v => v.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One || (v.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne && v.FromEndMember.GetEntityType() == v.ToEndMember.GetEntityType()))
            .Select(v => new { NavigationProperty = v, DependentProperties = v.GetDependentProperties().Take(2).ToArray() })
            .Where(v => v.DependentProperties.Length == 1)
            .ToDictionary(v => v.DependentProperties[0].Name, v => v.NavigationProperty);

        return new ReadOnlyDictionary<string, NavigationProperty>(result);
    }
}

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

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设置为其他/特殊的对象),或者-就像最初的问题一样-我们期望项一旦从父对象中删除就会删除。

我尝试过这些方法和其他方法,但没有一个很有效。由于这是谷歌上的第一个答案,我将在这里添加我的解。

对我来说很有效的方法是在提交期间将关系排除在外,这样EF就不会搞砸了。我通过在DBContext中重新找到父对象并删除它来做到这一点。因为重新找到的对象的导航属性都是空的,所以在提交过程中会忽略子对象的关系。

var toDelete = db.Parents.Find(parentObject.ID);
db.Parents.Remove(toDelete);
db.SaveChanges();

注意,这里假设外键设置为ON DELETE CASCADE,所以当父行被删除时,子行将被数据库清除。