【问题标题】:A relationship is in the Deleted state关系处于已删除状态
【发布时间】:2013-07-24 21:56:28
【问题描述】:

当我尝试清除集合(调用 .Clear)时,我收到以下异常:

保存不为其关系公开外键属性的实体时发生错误。 EntityEntries 属性将返回 null,因为无法将单个实体标识为异常源。通过在实体类型中公开外键属性,可以更轻松地处理保存时的异常。有关详细信息,请参阅 InnerException。

内部异常是:

“User_Availability”关联集中的关系处于“已删除”状态。给定多重约束,相应的“User_Availability_Target”也必须处于“已删除”状态。

用户看起来像这样:

....
ICollection<Availability> Availability { get; set; }

可用性如下所示:

int ID { get; set; }
User User { get; set; }
DateTime Start { get; set;
DateTime End { get; set; }

配置如下:

HasMany(x => x.Availability).WithRequired(x => x.User);
HasRequired(x => x.User).WithMany(x => x.Availability);

导致问题的代码是:

user.Availability.Clear();

我查看了其他替代方法,例如使用 DbSet 删除项目,但我觉得我的代码不会那么干净。有没有办法通过清除集合来完成此操作?

【问题讨论】:

    标签: c# entity-framework ef-code-first entity-framework-5


    【解决方案1】:

    我知道使它起作用的唯一方法是将关系定义为identifying relationship。需要将 AvailabilityUser 的外键作为外键引入模型中...

    public int ID { get; set; }
    public int UserID { get; set; }
    public User User { get; set; }
    

    ...并使其成为主键的一部分:

    modelBuilder.Entity<Availability>()
        .HasKey(a => new { a.ID, a.UserID });
    

    您可以扩展您的映射以包含此外键(为了明确起见,它不是必需的,因为 EF 会按照惯例识别它):

    modelBuilder.Entity<Availability>()
        .HasRequired(a => a.User)
        .WithMany(u => u.Availability)
        .HasForeignKey(a => a.UserID);
    

    (顺便说一句:您只需要从一侧配置关系。在您的问题中不需要同时具有这两个映射。)

    现在您可以使用user.Availability.Clear(); 清除集合,Availability 实体将从数据库中删除。

    【讨论】:

    • 感谢您发布此信息!当我对子对象运行更新时,由于 FK 属性和导航属性,我在同一列上获得了多个 SET 语句。是否有只生成一个 SET 语句的技巧?
    • @Thomas:我不知道。 SQL 生成几乎掌握在 EF 手中并且难以控制。我真的很惊讶您会为同一个 FK 列获得两个 SET 表达式。 EF应该知道FK属性和nav。属性表示相同的 FK 列和关系。
    • 你确定你的例子是正确的吗?你有 modelBuilder.Entity().HasRequired(x => x.User) ...不应该是 Entity() 的配置吗?我尝试了此配置,但收到一条错误消息:“操作失败:无法更改关系,因为一个或多个外键属性不可为空。...”。如果外键可以为空,也许这适用于多方面?
    • 解决了我的问题,但我得到了“当 IDENTITY_INSERT 设置为 OFF 时,无法在表 xxx 中插入标识列的显式值”。解决方案是将属性 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 添加到(在这种情况下)可用性的原始 ID 列中。
    • 注:正如 Derek 所说,&lt;User&gt; 确实应该是 &lt;Availability&gt;
    【解决方案2】:

    有一个技巧。您可以在不使用特殊 DbSet 的情况下删除实体:

    (this.dataContext as IObjectContextAdapter).ObjectContext.DeleteObject(entity);
    

    在清除之前对可用性集合中的每个项目执行此操作。这种方式不需要“识别关系”。

    【讨论】:

      【解决方案3】:

      如果有人在使用 SQLite 时遇到同样的问题:

      不幸的是,接受的答案不适用于 SQLite,因为 SQLite 不支持复合键的自动递增。

      您还可以覆盖数据库上下文中的 SaveChanges() 方法以删除子项:

      //// Long Version
      //var localChilds = this.SubCategories.Local.ToList();
      //var deletedChilds = localChilds.Where(w => w.Category == null).ToList();
      //foreach(var child in deletedChilds) {
      //   this.SubCategories.Remove(child);
      //}
      
      // Short in LINQ
      this.SubCategories.Local
          .Where(w => w.Category == null).ToList()
          .ForEach(fe => this.SubCategories.Remove(fe));
      #endregion
      

      请参阅 this great Blogpost 作为我的来源(不幸的是用德语写的)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多