I have created an asp.NET MVC web app with two models using EF Code First which have a 1-to-0..1 relationship.
public class ClassA
{
public int Id {get;set;}
//other properties
public virtual ClassB ClassB {get;set;}
}
public class ClassB
{
public int Id {get;set;}
//other properties
}
In my database, this successfully creates the two tables with ClassA having a Nullable FK for ClassB. This works great, except for the scenario in which a ClassA record is deleted. In that case, any associated ClassB record is left in the database. I know that I can manually remove them in the Delete POST method:
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
ClassA classA = context.ClassA.Include(c => c.ClassB).First(c => c.Id == id);
context.ClassB.Remove(classA.ClassB);
context.ClassA.Remove(classA);
context.SaveChanges();
}
I don't really like this approach because it relies on me not making a mistake and assumes that this method is the only way that a record might be deleted (there might also be direct DELETE SQL statements run against the database at some point).The example I've created here is simple, but my actual application involves a lot of models and associations and it has gotten quite complicated. While I like to think that I am infallible, I have learned from experience that I am not and would rather not rely on myself to ensure that records don't become orphaned =)
How can I force the database to enforce referential integrity so that when a ClassA is deleted, any ClassB's that have foreign keys in that ClassA are also deleted?
SOLVED (kinda)
As suggested by Gert, I used Fluent API to ensure that the right entity was set as the Principle and to set all of the relationships to cascade on delete. I did run into a few issues though, mostly due to there already being data in my database. Fortunately, I am at a point in development where I can simply delete all of the data; otherwise, I'm not sure how I would have fixed this.
First I tried to just add the Fluent API and update-database. I got an error that read in part: "Either the parameter @objname is ambiguous or the claimed @objtype (COLUMN) is wrong" which seemed to be due to EF trying to change the name of an existing FK column. That being the case, I resolved to use 2 migrations: One to remove existing relationships and another to add the newly reconfigured relationships. I had to do this in a rather specific series of events.
- Commented out all references to the affected relationships in my controller to avoid errors while updating.
Commented out the relationship in the Model.
public class ClassA { public int Id {get;set;} //other properties //public virtual ClassB ClassB {get;set;} }
- Add-Migration and Update-Database to remove the existing relationships.
- Undid all changes to the model and controller by uncommenting everything that I commented out in step 1 and 2.
Configured the new relationship in OnModelCreating as suggested by Gert to save the new relationships.
modelBuilder.Entity<ClassA>() .HasOptional(b => b.ClassB) .WithRequired() .Map(m => m.MapKey("ClassB_Id")) .WillCascadeOnDelete();
Add-Migration and Update-Database to create the new relationships. This step failed when there was existing data in my database. If I didn't have the option to simple clear the database of all data, I'm not sure how I would have accomplished this.