EF6.0 "由于一个或多个外键属性为非空值,关系无法更改"

37
如果我尝试删除一个“子”行,我总是会得到一个异常。以下是代码片段:
using (var context = new CompanyContext())
{
    ItemType itemType = context.ItemTypes.FirstOrDefault(i => i.Name == "ServerType");
    ItemTypeItem itemTypeItem = itemType.Items.FirstOrDefault(i => i.Name == "DatabaseServer");
    itemType.Items.Remove(itemTypeItem);
    context.SaveChanges(); <=== exception!
}

以下异常会在SaveChanges()方法中抛出。

“由于一个或多个外键属性是非空的,因此无法更改关系。当关系发生变化时,相关的外键属性被设置为null值。如果外键不支持null值,则必须定义新的关系,将外键属性分配给另一个非null值,或删除无关对象。”

实体配置
  public class ItemTypeConfiguration : NamedEntityConfiguration<ItemType>
  {
    public ConfigurationColumn ParentIDColumn;
    public ConfigurationColumn ValidationPatternColumn;
    public ItemTypeConfiguration() : base()
    {
      ParentIDColumn = new ConfigurationColumn() { Name = "ParentID", Ordinal = base.LastOrdinalPosition + 1 };
      ValidationPatternColumn = new ConfigurationColumn() { Name = "ValidationPattern", Length = 1024, Ordinal=base.LastOrdinalPosition + 2};
      this.Property(t => t.ParentID)
        .HasColumnName(ParentIDColumn.Name)
        .HasColumnOrder(ParentIDColumn.Ordinal);
      this.HasOptional(t => t.Parent).WithMany().HasForeignKey(u => u.ParentID).WillCascadeOnDelete(false);
      this.Property(t => t.ValidationPattern)
        .HasColumnName(ValidationPatternColumn.Name)
        .HasColumnOrder(ValidationPatternColumn.Ordinal)
        .HasMaxLength(ValidationPatternColumn.Length);
    }
...


  public class ItemTypeItemConfiguration : NamedEntityConfiguration<ItemTypeItem>
  {
    public ConfigurationColumn ItemTypeIDColumn;
    public ItemTypeItemConfiguration() : base()
    {
      ItemTypeIDColumn = new ConfigurationColumn(){Name="ItemTypeID", IsRequired=true, Ordinal= base.LastOrdinalPosition+1};
      this.Property(t => t.ItemTypeID)
        .HasColumnName(ItemTypeIDColumn.Name)
        .HasColumnOrder(ItemTypeIDColumn.Ordinal);
      this.HasRequired(t => t.ItemType).WithMany(t=>t.Items).HasForeignKey(u => u.ItemTypeID).WillCascadeOnDelete(true);
    }
...

我找到了这篇博客,但我没有看到 "DeleteObject" 方法。
可以参考以下链接:http://blog.clicdata.com/2013/07/04/the-operation-failed-the-relationship-could-not-be-changed-because-one-or-more-of-the-foreign-key-properties-is-non-nullable/ 有什么建议吗?谢谢。

5个回答

55

您需要删除ItemTypeItem。无法仅从Items列表中将其移除,因为它不能存在于自身,因为它具有对ItemType(ItemTypeID)的非空外键引用。

要删除ItemTypeItem,请添加

context.Entry(itemTypeItem).State = EntityState.Deleted;

3
这很奇怪,因为我可以在不使用EntityState.Added的情况下添加项目...但这样做是有效的,谢谢!现在我需要找出如何更改我的仓储以实现相同的结果。我只是从上下文中进行了简单的测试。EF就像一个黑匣子,还有很多要学习的东西!!! - Max
2
只有在我读了下面@Gerry的答案后才明白这个答案...关键是要从“context”中删除它,而不是从实体父级中删除... - Rosdi Kasim
我同意@Max的看法,当你添加新实体时,你只需要将其添加到集合中,db上下文会为你跟踪它。你不需要将状态设置为“Added”。然而,对于删除操作,你不能仅仅从集合中删除,你必须从上下文中删除,并标记状态为删除...这很令人困惑。 - LP13

53

在实体框架6.0中,如果您从主上下文集中删除实体,它将起作用。例如,要删除投资实体,您应该执行以下操作:

context.Investments.Remove(entity);
context.SaveChanges();

这与试图将实体从其父级/所有者中移除不同,如下所示:

bankAccount.Investments.Remove(entity);
context.SaveChanges();

这将抛出上面列出的“无法更改关系”的异常。希望这能帮到你。


26
在实体 6.0 中,存在以下区别:
context.Investments.Remove(entity);

context.Entry(entity).State = EntityState.Deleted;

当使用第一个选项并启用级联删除时,EF会自动执行子集合的必要删除操作。当使用第二个选项时,EF不会处理必要的删除操作,而是让您处理这些子对象的重新绑定/删除。


2
救了我的一天。我已经正确配置了级联删除,但是我仍然遇到了相同的异常。更令人惊讶的是,一个使用完整的 SQL Server 作为后端的绿色单元测试证明了级联删除的启动。这个评论让我思考并帮助我解决了问题:当实体之前从此上下文实例中加载时,首选context.Set<T>.Remove(entity),而当只提供键和构造存根实体时,context.Entry(entity).State = Deleted可行。 - Marc Wittke

1
这个问题是因为我们试图删除父表,但子表数据仍然存在。我们通过级联删除来解决这个问题。
在dbcontext类的Create方法中的模型中。
 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();

级联删除选项可以使用这个简单的代码删除父表和与其相关的子表。尝试使用这种简单的方法。
使用Remove Range可以删除数据库中的记录列表。谢谢。

实体框架(Entity Framework)默认设置.WillCascadeOnDelete(true)吗? - crh225

0

其他回答对于错误发生的解释是正确的,但实际上,当在父对象的子集合上调用.Remove时,可以完全删除子对象,你不需要直接访问数据库上下文中的子实体表并从中删除它。

要使其工作,你需要设置一个身份关系,如此答案所述


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接