【问题标题】:EntityFramework: How to configure Cascade-Delete to nullify Foreign KeysEntityFramework:如何配置级联删除以使外键无效
【发布时间】:2013-03-05 14:28:16
【问题描述】:

EntityFramework 的文档指出以下行为是可能的:

如果依赖实体上的外键可以为空,Code First 会 没有在关系上设置级联删除,并且当主体是 删除的外键将设置为null。

(来自http://msdn.microsoft.com/en-us/jj591620

但是,我无法实现这样的行为。

我有以下使用代码优先定义的实体:

public class TestMaster
{
    public int Id { get; set; }
    public string Name { get; set; }        
    public virtual ICollection<TestChild> Children { get; set; }       
}

public class TestChild
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual TestMaster Master { get; set; }
    public int? MasterId { get; set; }
}

Fluent API 映射配置如下:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<TestMaster>()
                    .HasMany(e => e.Children)
                    .WithOptional(p => p.Master).WillCascadeOnDelete(false);

        modelBuilder.Entity<TestChild>()
                    .HasOptional(e => e.Master)
                    .WithMany(e => e.Children)
                    .HasForeignKey(e => e.MasterId).WillCascadeOnDelete(false);
    }

外键可以为空,导航属性映射为可选,因此我希望级联删除能够按照 MSDN 的描述工作 - 即取消所有子项的 MasterID,然后删除 Master 对象。

但是当我实际尝试删除时,我得到了 FK 违规错误:

 using (var dbContext = new TestContext())
        {
            var master = dbContext.Set<TestMaster>().Find(1);
            dbContext.Set<TestMaster>().Remove(master);
            dbContext.SaveChanges();
        }

在 SaveChanges() 上它会抛出以下内容:

System.Data.Entity.Infrastructure.DbUpdateException : An error occurred while updating the entries. See the inner exception for details.
----> System.Data.UpdateException : An error occurred while updating the entries. See the inner exception for details.
----> System.Data.SqlClient.SqlException : The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.TestChilds_dbo.TestMasters_MasterId". The conflict occurred in database "SCM_Test", table "dbo.TestChilds", column 'MasterId'.
The statement has been terminated.

是我做错了什么还是我误解了 MSDN 所说的内容?

【问题讨论】:

    标签: entity-framework entity-framework-5


    【解决方案1】:

    它确实像描述的那样工作,但 MSDN 上的文章没有强调 它只有在子实体也加载到上下文时才有效,而不仅仅是父实体。因此,不要使用Find(仅加载父级),您必须使用带有Include 的预加载(或任何其他将子级加载到上下文中的方式):

    using (var dbContext = new TestContext())
    {
        var master = dbContext.Set<TestMaster>().Include(m => m.Children)
            .SingleOrDefault(m => m.Id == 1);
        dbContext.Set<TestMaster>().Remove(master);
        dbContext.SaveChanges();
    }
    

    这将从数据库中删除主节点,将 Child 实体中的所有外键设置为 null,并将子节点的 UPDATE 语句写入数据库。

    【讨论】:

    • 粗体字救了我。延迟加载很棒,但请确保在删除之前加载子对象...
    • 我正在尝试这个例子。但在“包含”中出现错误。它说“无法将lamba表达式转换为类型“字符串”,因为它不是委托类型”......我错过了什么?谢谢
    【解决方案2】:

    在遵循@Slauma 的精彩回答后,我仍然遇到与 OP 相同的错误。

    所以不要像我一样天真,以为下面的例子最终会得到相同的结果。

    dbCtx.Entry(principal).State = EntityState.Deleted;
    dbCtx.Dependant.Where(d => d.PrincipalId == principalId).Load();
    
    // code above will give error and code below will work on dbCtx.SaveChanges()
    
    dbCtx.Dependant.Where(d => d.PrincipalId == principalId).Load();
    dbCtx.Entry(principal).State = EntityState.Deleted;
    

    首先将子项加载到上下文中将实体状态设置为已删除(如果您这样做的话)。

    【讨论】:

      猜你喜欢
      • 2019-12-24
      • 2015-10-06
      • 1970-01-01
      • 2016-04-02
      • 2021-12-10
      • 2015-03-04
      • 1970-01-01
      • 2020-10-30
      • 2012-09-21
      相关资源
      最近更新 更多