开发者

Remove Dependent Entity When Relationship Deleted

开发者 https://www.devze.com 2023-04-02 19:32 出处:网络
Say I have two entities like so: public class Response { public int Id { get; set; } public int PatientId { get; set; }

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 re开发者_运维问答lationship, 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.


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


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


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号