【问题标题】:EF6.0 "The relationship could not be changed because one or more of the foreign-key properties is non-nullable"EF6.0“无法更改关系,因为一个或多个外键属性不可为空”
【发布时间】:2013-10-19 23:12:00
【问题描述】:

如果我尝试删除“子”行,我总是会遇到异常。这是一个片段:

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() 方法抛出以下异常。

“由于一个或多个外键属性不可为空,因此无法更改关系。当对关系进行更改时,相关的外键属性设置为空值。如果外键属性-key 不支持空值,必须定义新的关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。”

实体配置

  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/

有什么想法吗?谢谢。

【问题讨论】:

标签: entity-framework fluent-interface


【解决方案1】:

您需要删除 ItemTypeItem。 不能仅将其从 Items 列表中删除,因为它不能单独存在,因为它具有引用 ItemType (ItemTypeID) 的不可为空的外键。

要删除 ItemTypeItem 添加

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

【讨论】:

  • 这很奇怪,因为我可以在不执行 EntityState.Added 的情况下添加项目......但是那个工作人员,谢谢!现在我需要找出如何更改我的存储库以实现相同的目标。我只直接从上下文中测试。 EF 就是这么一个黑盒子,要学的东西太多了!!!
  • 我只有在阅读下面@Gerry 的答案后才能理解这个答案..,关键是从context 中删除它,而不是从实体父级中删除...
  • 我同意@Max 的观点,在添加新实体时,您只需将其添加到集合中,数据库上下文就会为您处理。您不必将状态设置为 Added 但是对于删除,您不能只从集合中删除,您必须从上下文中将其删除并将状态标记为已删除......它令人困惑
【解决方案2】:

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

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

这与尝试从其父/所有者中删除实体不同,如下所示:

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

这将抛出上面列出的无法更改关系的异常。希望这会有所帮助。

【讨论】:

    【解决方案3】:

    在实体 6.0 中存在以下区别:

    context.Investments.Remove(entity);
    

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

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

    【讨论】:

    • 拯救了我的一天。我配置了正确的级联删除,但我得到了同样的异常。更令人惊讶的是,使用完整的 SQL Server 作为后端的绿色单元测试证明,级联删除开始了。这条评论让我思考并帮助我解决了这个问题: context.Set.Remove(entity) 是首选时实体之前是从这个上下文实例中加载的,而 context.Entry(entity).State = Deleted 在只提供一个键并构造一个存根实体时起作用。
    【解决方案4】:

    出现这个问题是因为我们尝试删除父表时仍然存在子表数据。我们通过级联删除解决了这个问题。

    在 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();
    

    级联删除选项使用此简单代码删除父表以及与父相关的子表。用这种简单的方式尝试一下。

    Remove Range 用于删除数据库中的记录列表 谢谢

    【讨论】:

    • Entity框架是否默认设置.WillCascadeOnDelete(true)?
    【解决方案5】:

    描述错误发生原因的其他答案是正确的,但是当在父对象的子对象集合上调用 .Remove 时,实际上可以让 EF 完全删除子对象,您 不需要直接进入数据库上下文中的子实体表并从中删除。

    要让它发挥作用,您需要建立一个识别关系,as described in this answer

    【讨论】:

      猜你喜欢
      • 2015-12-16
      • 2014-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多