How do you delete rows in tables using Entity Framework (Core), when you have two tables that are referencing each other in both directions?
Consider there classes:
public class ImageSeries
{
int Id { get set; }
int? MainImageId { get set; } // Reference to Image.Id
}
public class Image
{
int Id { get set; }
int ImageSeriesId { get set; } // Reference to ImageSeries.Id
}
Both have corresponding tables in the database. Zero to many images can be connected to an image series, but the image series can also reference one of those images as its main image.
In the database, the tables have constraints such that an image must reference an existing image series. The image series can, but doesn't have to, reference an image. There is no cascade delete configured.
When deleting an image series, the images connected to that should also be deleted. Because of the constraints, I want to delete the images before deleting the image series. However, I cannot delete the image that the image series references as its main image…
The solution, I believe, is this:
- Set MainImageId of ImageSeries to null.
- Delete all Images referencing the ImageSeries.
- Delete the ImageSeries.
I want to do this in a single transaction. Using Entity Framework, I'm not explicitly using a transaction in my code, but I'm only calling SaveChanges() once which I believe will have the same effect.
Here some code:
public void DeleteImageSeries(int imageSeriesToRemoveId)
{
var imageSeries = dbContext.ImageSeries.First(is => is.Id == imageSeriesToRemoveId);
imageSeries.MainImageId = null;
var images = dbContext.Images.Where(i => i.ImageSeriesId == imageSeriesToRemoveId);
foreach (var image in images)
{
dbContext.Remove(image);
}
dbContext.Remove(imagesSeries);
dbContext.SaveChanges();
}
This does not work. MainImageId of the image series is not set to null before the images are being deleted, causing an exception to be thrown.
How do I make EntityFramework set MainImageId of ImageSeries to null before deleting the images? Preferably without calling SaveChanges() multiple times, and also without using a transaction (my real code I have this problem in is considerably more complex and would be hard to use a transaction in).
do this in a single transaction.already done, which means your method is buggy.SaveChangeswill persist all pending changes, including anyINSERTs andUPDATEs, inside a single internal database transaction. EF Core is an ORM, not a database driver and deals with objects, not tables. When you callDbContext.Removeyou aren't deleting anything, you change that object's state, so it will be deleted whenSaveChangesis called. Your entities have no object relations (eg Image.ImageSeries, or ImageSeries.Images), otherwise you wouldn't have to load entities only to delete them.Imageobjects would be deleted automatically. In the doc example, whereBloghas aPostscollection,context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).FirstAsync()will load a blog with its posts, no extra query required.context.Remove(blog);will result in a cascading delete of the blog's PostsIsMainForSerieswhere only one row for each series can = 1?