2013-03-05 43 views
30

的EntityFramework的文档指出以下行为是可能的:EntityFramewok:如何配置级联删除,以抵消外键

If a foreign key on the dependent entity is nullable, Code First does not set cascade delete on the relationship, and when the principal is deleted the foreign key will be set to 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; } 
} 

这里是流利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的说法?

回答

42

它的确如所描述的那样工作,但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语句写入数据库。

+7

粗体文本救了我。延迟加载很好,但确保子对象在删除之前加载... – amaters 2013-12-17 20:14:49

+0

我正在尝试此示例。但在“包含”中出现错误。它说“不能将lamba表达式转换为类型”字符串“,因为它不是委托类型”....我缺少什么?谢谢 – Diego 2017-05-19 12:56:09

0

以下@ 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; 

首先负载孩子的来龙去脉以前设置实体状态来删除(如果你正在做这样的说法)。