无法将外键设置为空

时间:2013-09-30 22:25:29

标签: c# entity-framework

我有一个可选的外键,我试图将其设置为null。无论我尝试过什么,在SaveChanges()上,update语句将外键设置为前一个值而不是null。

Simplified Child Class:

public class Child
{
    [Key, Column(Order = 0), ScaffoldColumn(false)]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [ForeignKey("Parent")]
    public int? ParentId { get; set; }

    public virtual Parent Parent { get; set; }
}

简化父类:

public class Parent
{
    [Key, Column(Order = 0), ScaffoldColumn(false)]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public virtual ICollection<Child> Children { get; set; }
}

我尝试过的事情:

  1. 加载Child对象并将ParentId设置为null并将Parent设置为null
  2. 加载Child对象并将ParentId设置为null并强制修改实体状态
  3. 加载包含Parent对象的Child对象,然后将值设置为null并强制修改实体状态
  4. 从Parent对象
  5. 加载Parent对象,然后加载Child对象和.Remove(child)
  6. 从Parent加载Parent对象,然后加载Child对象和.Remove(child),并将Child.ParentId设置为null,将Child.Parent设置为null。
  7. 目前我有:

    public void RemoveChildFromParent(int childId, int parentId)
    {
        Parent parent = _context.Parents.Include(x => x.Children).FirstOrDefault(u => u.Id == parentId);
        Child child = parent.Children.SingleOrDefault(u => u.Id == childId);
        parent.Children.Remove(child);
        child.ParentId = null;
        child.Parent = null;
        child.StateOfEntity = StateOfEntity.Modified;
    
        _context.ApplyStateChanges();
        _context.SaveChanges();
    }
    

    在保存更改时,SQL Update语句仍然将Child对象上的ParentId设置为旧值,我收到此错误:

    System.InvalidOperationException was unhandled by user code
      HResult=-2146233079
      Message=The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
      Source=System.Data.Entity
      StackTrace:
           at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)
           at System.Data.Entity.Internal.InternalContext.SaveChanges()
           at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
           at System.Data.Entity.DbContext.SaveChanges()
           at Insight.DataLayer.InsightContext.SaveChanges()
           at Insight.DataLayer.ChildRepository.RemoveChildFromParent(Int32 childId, Int32 parentId)
           at Insight.BusinessLayer.ParentManager.RemoveChild(Int32 id, Int32 parentId)
           at Insight.PresentationLayer.Controllers.ParentController.RemoveChild(Int32 id, Int32 parentId)
           at lambda_method(Closure , ControllerBase , Object[] )
           at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
           at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
           at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass37.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33()
           at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49()
      InnerException: 
    

    此外,不确定是否重要,但我有LazyLoadingEnabled = false和AutoDetectChangesEnabled = false。

2 个答案:

答案 0 :(得分:1)

我不确定是否有一个“优雅”的解决方案(可能会更改表格结构?),但我决定使用DbContext.Database.ExecuteSqlCommand()而不是花更多时间在这个小问题上。手动编写更新语句。

这绝对感觉像是在实体框架方法中的一种解决方法,但它仅限于这种情况,只需要很少的时间,并按预期工作。

答案 1 :(得分:0)

  

另外,不确定这是否重要,但我有...... AutoDetectChangesEnabled = false

是的,这很重要。您是否默认禁用自动更改检测(例如在您的上下文构造函数中)?这很危险,因为如果禁用自动检测,您必须知道并理解何时需要手动调用更改检测 - 这是not a trivial thing。通常只有在您确定它不会导致意外结果并且您确实需要它时才设置AutoDetectChangesEnabled = false(通常出于性能原因,在运行具有许多实体更新,插入或删除的批量操作时)。我肯定会默认<{1}}

我不知道true究竟做了什么(似乎是一个自定义方法),但查询之后所有其他代码行都不会调用任何EF方法,直到_context.ApplyStateChanges(和5描述的程序既不是其中一种情况(如上面链接的博客文章中所述),其中禁用自动更改检测无需进一步处理。

要解决此问题,您可以尝试在SaveChanges之前(或可能在_context.DetectChanges();之前)在代码段中调用SaveChanges。但是,使用所有子项加载父项的过程非常昂贵,最简单的解决方案就是加载子项,将FK设置为​​ApplyStateChanges并保存更改 - 所有这些启用自动更改检测:

null