I’m trying to remove a child entity from a parent collection navigation property. There is a one-to-many relationship set up b/t parent and child. Once I remove the child, I want the database to remove the assoc. child record from the database rather than orphaning that record by nullifying the foreign key.
Is there any way to do this without having to explicitly delete the child via the child DbSet in the DBContext?
I’ve seen other posts related to this topic, but I thought I’d distill the code down to a simpler test case:
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using NUnit.Framework;
namespace basic_tests
{
[TestFixture]
public class OneToManyTests
{
#region Setup/Teardown
[SetUp]
public void SetUp()
{
_context = new Context();
Database.SetInitializer(new DataInitializer());
}
#endregion
private Context _context;
[Test]
public void CanRemoveChildThroughParent()
{
/**
this throws : "System.Data.Entity.Infrastructure.DbUpdateException : An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for
details.System.Data.UpdateException : A relationship from the 'Child_MyParent' AssociationSet is in the 'Deleted' state. Given multiplicity constraints, a
corresponding 'Child_MyParent_Source' must also in the 'Deleted' state.
**/
var parent = _context.Parents.FirstOrDefault();
var firstChild = parent.MyChildren.FirstOrDefault();
parent.MyChildren.Remove(firstChild);
_context.SaveChanges();
var parentRefresh = new Context().Parents.FirstOrDefault();
Assert.AreEqual(1, parentRefresh.MyChildren.Count);
var childrenCount = new Context().Children.Count();
Assert.AreEqual(1, childrenCount);
}
}
public class Parent
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Child> MyChildren { get; set; }
}
public class Child
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Parent MyParent { get; set; }
}
public class Context : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Child>()
.HasRequired(c => c.MyParent)
.WithMany(p => p.MyChildren)
.WillCascadeOnDelete(true);
}
}
public class DataInitializer : DropCreateDatabaseAlways<Context>
{
protected override void Seed(Context context)
{
for (var i = 0; i < 2; i++)
{
context.Children.Add(new Child
{
Name = "child" + i
});
}
var parent = new Parent { Name = "parent", MyChildren = context.Children.Local.ToList() };
context.Parents.Add(parent);
base.Seed(context);
}
}
}
In your model: No, there is no other way. You have to call:
I recently have learned that there is one exception which causes an automatic delete in the database when you remove the child from the parent collection. That’s the so-called Identifying relationship which requires that the foreign key property in the child refering to the parent must be part of the (composite) primary key of the child:
In this case your code would indeed delete the child from the database. It is explained here (last section at the bottom): http://msdn.microsoft.com/en-us/library/ee373856.aspx