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

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

我不太理解这句话:

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

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

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

代码行可以提炼为:

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

当前回答

我不知道为什么其他两个答案这么受欢迎!

我相信您认为ORM框架应该处理它是正确的——毕竟,这是它承诺交付的。否则,您的域模型就会被持久性问题所破坏。如果你正确地设置了级联设置,NHibernate就能很好地管理它。在实体框架中也有可能,他们只是希望你在建立数据库模型时遵循更好的标准,特别是当他们不得不推断应该做什么级联时:

您必须使用“识别关系”来正确地定义父-子关系。

如果你这样做,实体框架知道子对象是由父对象标识的,因此它必须是一个“级联删除孤儿”的情况。

除了上面的,你可能需要(从NHibernate的经验)

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

而不是完全替换列表。

更新

@Slauma的评论提醒我,分离实体是整体问题的另一部分。为了解决这个问题,您可以采用使用自定义模型绑定器的方法,通过尝试从上下文加载模型来构造模型。这篇博客文章展示了我的意思。

其他回答

你必须手动清除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 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),这是可以的。

我希望这能有所帮助。

You should delete old child items thisParent.ChildItems one by one manually. Entity Framework doesn't do that for you. It finally cannot decide what you want to do with the old child items - if you want to throw them away or if you want to keep and assign them to other parent entities. You must tell Entity Framework your decision. But one of these two decisions you HAVE to make since the child entities cannot live alone without a reference to any parent in the database (due to the foreign key constraint). That's basically what the exception says.

Edit

如果子项目可以添加,更新和删除,我会做什么:

public void UpdateEntity(ParentItem parent)
{
    // Load original parent including the child item collection
    var originalParent = _dbContext.ParentItems
        .Where(p => p.ID == parent.ID)
        .Include(p => p.ChildItems)
        .SingleOrDefault();
    // We assume that the parent is still in the DB and don't check for null

    // Update scalar properties of parent,
    // can be omitted if we don't expect changes of the scalar properties
    var parentEntry = _dbContext.Entry(originalParent);
    parentEntry.CurrentValues.SetValues(parent);

    foreach (var childItem in parent.ChildItems)
    {
        var originalChildItem = originalParent.ChildItems
            .Where(c => c.ID == childItem.ID && c.ID != 0)
            .SingleOrDefault();
        // Is original child item with same ID in DB?
        if (originalChildItem != null)
        {
            // Yes -> Update scalar properties of child item
            var childEntry = _dbContext.Entry(originalChildItem);
            childEntry.CurrentValues.SetValues(childItem);
        }
        else
        {
            // No -> It's a new child item -> Insert
            childItem.ID = 0;
            originalParent.ChildItems.Add(childItem);
        }
    }

    // Don't consider the child items we have just added above.
    // (We need to make a copy of the list by using .ToList() because
    // _dbContext.ChildItems.Remove in this loop does not only delete
    // from the context but also from the child collection. Without making
    // the copy we would modify the collection we are just interating
    // through - which is forbidden and would lead to an exception.)
    foreach (var originalChildItem in
                 originalParent.ChildItems.Where(c => c.ID != 0).ToList())
    {
        // Are there child items in the DB which are NOT in the
        // new child item collection anymore?
        if (!parent.ChildItems.Any(c => c.ID == originalChildItem.ID))
            // Yes -> It's a deleted child item -> Delete
            _dbContext.ChildItems.Remove(originalChildItem);
    }

    _dbContext.SaveChanges();
}

注意:这不是测试。它假设子项集合的类型是ICollection。(我通常有IList,然后代码看起来有点不同。)为了保持简单,我还去掉了所有存储库抽象。

我不知道这是否是一个好的解决方案,但我相信必须按照这些思路做一些艰苦的工作,以处理导航集合中的各种更改。我也很乐意看到一种更简单的方法。

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

在模型中创建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();

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

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