0

说我有两个实体,像这样:删除相关实体在关系删除

public class Response 
{ 
    public int Id { get; set; } 
    public int PatientId { get; set; } 
    public virtual Patient Patient { get; set; } 
    public string Text { get; set; } 
} 

public class Patient 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual ICollection<Response> Responses { get; set; } 
} 

我希望能够调用

Patient.Responses.Remove(someResponse); 

而且具有实体删除不但关系,但响应实体以及。目前如果我只是删除关系,我得到以下错误:

System.InvalidOperationException:操作失败:由于一个或多个外键属性是不可空的,所以无法更改关系。当对关系进行更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新的关系,必须为外键属性指定另一个非空值,或者必须删除不相关的对象。

阅读这篇博客http://blogs.msdn.com/b/dsimmons/archive/2010/01/31/deleting-foreign-key-relationships-in-ef4.aspx我意识到,我可以有以下映射实现这一点:

modelBuilder.Entity<Response>().HasKey(m => new { m.Id, m.PatientId }); 

但我不想改变我的主键。我想要做的是覆盖DbContext.SaveChanges()并标记删除任何Responses,其中患者关系已被删除。我尝试这样做:

public override int SaveChanges() 
{ 
    // Need to manually delete all responses that have been removed from the patient, otherwise they'll be orphaned. 
    var orphanedResponses = ChangeTracker.Entries().Where(
    e => e.State == EntityState.Modified && 
     e.Entity is Response && 
     e.Reference("Patient").CurrentValue == null); 
    foreach (var orphanedResponse in orphanedResponses) 
    { 
    Responses.Remove(orphanedResponse.Entity as Response); 
    } 

    return base.SaveChanges(); 
} 

但我发现这是可能的,只有Response.PatientId设置附加一个响应,而不是Response.Patient,实体不会有,所以我的代码认为它已经成为孤儿,并应加载Response.Patient财产被删除。

总之

我想知道的是我如何可以告诉大家,实体已经被修改,因为它的FK关系已被删除。

回答

1

我想我的问题是不是与代码,而是与我如何承担实体的attach()方法的工作。我认为如果我附加了PatientId设置但不包含Patient属性的响应,那么实体会为我填充Patient属性。

事实上,我认为会发生的是实体附加它,那么如果我将该实体标记为已修改并保存它,实体将看到空Patient属性并假定我想删除该关系,因此会抛出错误,因为它将被孤立(不能为空Response.PatientId)。所以也许一切都按照设计和我的SaveChanges()解决方案工作。

1

您需要配置映射,以便发生级联删除。为此,您需要将模型与WillCascadeOnDelete映射到true

public class MyContext : DbContext 
{ 
    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<Patient>() 
      .HasMany(patient=> patient.Responses) 
      .WithRequired(response => response.Patient) 
      .HasForeignKey(response => response.PatientId) 
      .WillCascadeOnDelete(true); 
    } 
} 
+0

谢谢,但级联删除只会删除父母被删除的权利?我不删除病人,我只是想从Patient.Response中删除响应,并让实体删除响应。 – phdesign

2

使用这个代替:

public override int SaveChanges() 
{ 
    var responses = Responses.Local.Where(r => r.Patient == null); 
    foreach (var response in responses.ToList()) 
    { 
     Responses.Remove(response); 
    } 

    return base.SaveChanges(); 
} 
+0

感谢Ladislav,这是另一种实现我所做的事情的方式。这将删除所有正在跟踪的响应,而不管它们是否被添加或修改,添加的实体是否有可能具有响应。PatientId设置但不是Response.Patient属性?那么实体不会为您填充Patient属性,并且在这种情况下您将删除有效的条目?我只是在这里猜测。 – phdesign

+0

更新:在前一段时间实施此版本后,有一天我不得不找出性能问题并意识到这是原因。如果您添加大量记录并一次保存一条记录,Responses.Local可能会大量增加。我现在使用ChangeTracker恢复到我的原始代码。 – phdesign