【问题标题】:EF code-first, cannot delete foreign key relationEF 代码优先,不能删除外键关系
【发布时间】:2013-12-25 07:39:38
【问题描述】:

这里显示了两个模型:

public class Application
{
  public string Name { get; set; }
  public virtual ICollection<ApplicationTransaction> ApplicationTransactions { get; set; }
}

public class ApplicationTransaction
{
  public long ApplicationId { get; set; }
  public virtual Application Application { get; set; }
}

我尝试使用此代码删除 Application 中的所有 ApplicationTransaction

var app = _repository.Get<Application>(i => i.Id == 1);
app.ApplicationTransactions.Clear();
Context.SaveChanges();

但是当上下文去保存更改时,会发生错误:

操作失败:无法更改关系,因为 一个或多个外键属性不可为空。当一个 对关系进行更改,相关的外键属性是 设置为空值。如果外键不支持空值, 必须定义一个新的关系,外键属性必须是 分配了另一个非空值,或者不相关的对象必须是 已删除。

【问题讨论】:

  • 代码中显示的模型与 db 不同,我知道。我显示模型简介

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


【解决方案1】:

我遇到了完全相同的问题,这是解决方案:

技巧:在设置父子关系时,您必须在子节点上创建一个“复合”键。这样,当您告诉父级删除 1 个或所有子级时,相关记录实际上会从数据库中删除。

使用 Fluent API 配置复合键:

modelBuilder.Entity<Child>.HasKey(t => new { t.ParentId, t.ChildId });

然后,删除相关的孩子:

var parent = _context.Parents.SingleOrDefault(p => p.ParentId == parentId);

var childToRemove = parent.Children.First(); // Change the logic 
parent.Children.Remove(childToRemove);

// or, you can delete all children 
// parent.Children.Clear();

_context.SaveChanges();

完成!

【讨论】:

    【解决方案2】:

    仅仅清除相关实体的集合是不够的,因为每个相关实体都存储了一个指向Application实体的外键;因此您需要删除相关实体而不是清除相关实体的集合。

    foreach (ApplicationTransaction applicationTransaction in app.ApplicationTransactions.ToList())
        context.DeleteObject(applicationTransaction);
    

    【讨论】:

    • 如果一个Application有千个Application Transaction,那么EF必须为每条记录生成千个查询。
    • @Behrooz 是的,但这就是它的工作原理。清除集合时,EF 不会执行一次性删除(就像 NHibernate 可以做的那样)。顺便说一句,DbContext 语法是 `context.ApplicationTransactions.Remove(applicationTransaction);
    • 不,您必须在孩子身上设置一个复合键。这样,当您从其父项中删除子项时,EF 将删除关系并从数据库中删除子记录。如果您不设置复合键,它只会尝试删除关系。
    【解决方案3】:

    也许您可以像这样使用Fluent API 手动创建关系:

     modelBuilder.Entity<Application>()
                .HasOptional(a => a.ApplicationTransactions)
                .WithOptionalPrincipal(a => a.Application);
    

    那么外键列将可以为空

    【讨论】:

    • 我认为 WithOptionalPrincipal 用于一对一的兴高采烈,但这是一对多的关系
    【解决方案4】:

    在您的“保存应用程序”方法中,您可以有一些类似这样的逻辑:

    var modifiedAppplicationTransactions = context.ChangeTracker.Entries<ApplicationTransactions>().Where(x => x.State == EntityState.Modified);
    foreach (var item in modifiedAppplicationTransactions )
    {
        if (item.Entity.ApplicationId == null)
        {
            context.Entry(item.Entity).State = EntityState.Deleted;
        }
    }
    context.SaveChanges():
    

    【讨论】:

      猜你喜欢
      • 2018-05-31
      • 2018-02-13
      • 2016-01-31
      • 2015-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多