删除关系时删除依赖实体

发布于 2024-12-03 07:35:36 字数 1832 浏览 1 评论 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() 并标记删除已删除患者关系的任何响应。我尝试了这个:

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 关系已被删除。

Say I have two entities like so:

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; }
}

I want to be able to call

Patient.Responses.Remove(someResponse);

And have entity delete not only the relationship but the Response entity as well. At present if I just delete the relationship I get the following error:

System.InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.

Reading this blog post http://blogs.msdn.com/b/dsimmons/archive/2010/01/31/deleting-foreign-key-relationships-in-ef4.aspx I realised I can achieve this by having the following mapping:

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

But I don't want to change my primary key. What I want to do is override DbContext.SaveChanges() and mark for deletion any Responses where the Patient relationship has been deleted. I tried this:

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();
}

But I found it's possible to attach a Response with only Response.PatientId set and not Response.Patient, entity wont have loaded the Response.Patient property so my code thinks it's been orphaned and should be deleted.

In summary

What I want to know is how can I can tell that an entity has been modified because it's FK relationship has been removed.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

蓝海 2024-12-10 07:35:36

使用这个代替:

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();
}

Use this instead:

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();
}
晨曦÷微暖 2024-12-10 07:35:36

您需要配置映射以便发生级联删除。为此,您需要将带有 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);
    }
}

You need to configure the mappings such that a cascade delete will occur. To do that you need to map the model with WillCascadeOnDelete to 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);
    }
}
寄风 2024-12-10 07:35:36

我认为我的问题不在于代码,而在于我假设实体的 Attach() 方法如何工作。我假设如果我附加设置了 PatientId 但没有设置 Patient 属性的响应,那么实体将为我填充 Patient 属性。

事实上,我认为发生的情况是实体按原样附加它,然后如果我将该实体标记为已修改并保存它,实体会看到 null Patient 属性并假设我要删除关系,因此会抛出错误,因为它将被孤立(不能将 Response.PatientId 设为 null)。因此,也许一切都按设计工作,并且我的 SaveChanges() 解决方案有效。

I think my problem is not with the code but rather with how I assume entity's Attach() method works. I assumed that if I attach a response with PatientId set but not Patient property then entity would populate the Patient property for me.

In fact what I think happens is entity attaches it as it is, then if I mark that entity as modified and save it, entity sees the null Patient property and assumes I want to remove the relationship, so throws an error because it would be orphaned (can't null Response.PatientId). So perhaps everything is working as designed and my SaveChanges() solution works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文