Link to home
Start Free TrialLog in
Avatar of Marco Medina
Marco Medina

asked on

DELETE statement conflicted with the REFERENCE constraint when updating an entity with a many 2 many relationship (JOIN TABLE)

I have 2 entities bounded together with a join table. Creating the article works fine. Updating the article entity throws this exception:

SqlException: The DELETE statement conflicted with the REFERENCE constraint "FK_ArticleGroups_Articles_ArticleID". The conflict occurred in database "myDB", table "dbo.ArticleGroups", column 'ArticleID'. The statement has been terminated.

Below is my classes and setup

User.cs

public int UserId { get; set; }
public string FullName { get; set; }
public virtual ICollection<GroupUser> GroupUsers { get; set; } = new List<GroupUser>();

Open in new window


Article.cs

public class Article
{
    public int ArticleId { get; set; }
    public string Title { get; set; }
    public virtual ICollection<ArticleGroup> ArticleGroups { get; set; } = new List<ArticleGroup>();
}

Open in new window


Group.cs

public class Group
{
   public int GroupId { get; set; }
   public string GroupName { get; set; }
   public virtual ICollection<ArticleGroup> ArticleGroups { get; set; } = new List<ArticleGroup>();
   public virtual ICollection<GroupUser> GroupUsers { get; set; } = new List<GroupUser>();
}

Open in new window


Join Tables

GroupUser.cs

public class GroupUser
{
        public int UserId { get; set; }

        public User User { get; set; }

        public int GroupId { get; set; }

        public Group Group { get; set; }
}

Open in new window



ArticleGroup.cs

public class ArticleGroup
{
   public int ArticleId { get; set; }
   public Article Article { get; set; }
   public int GroupId { get; set; }
   public Group Group { get; set; }
}

Open in new window


AppDbContext.cs

protected override void OnModelCreating(ModelBuilder modelBuilder)
{ 
    .....

    modelBuilder.Entity<GroupUser>()
                .HasKey(e => new { e.UserId, e.GroupId }); 

            modelBuilder.Entity<GroupUser>()
                .HasOne(e => e.User)
                .WithMany(e => e.GroupUsers)
                .HasForeignKey(e => e.UserId)
                .OnDelete(DeleteBehavior.Restrict);

            modelBuilder.Entity<GroupUser>()
                .HasOne(e => e.Group)
                .WithMany(e => e.GroupUsers)
                .HasForeignKey(e => e.GroupId)
                .OnDelete(DeleteBehavior.Restrict);

    modelBuilder.Entity<ArticleGroup>()
            .HasKey(e => new { e.ArticleID, e.GroupId });

        modelBuilder.Entity<ArticleGroup>()
            .HasOne(e => e.Article)
            .WithMany(e => e.ArticleKbGroups)
            .HasForeignKey(e => e.ArticleID)
            .OnDelete(DeleteBehavior.Restrict);

        modelBuilder.Entity<ArticleGroup>()
            .HasOne(e => e.Group)
            .WithMany(e => e.ArticleGroups)
            .HasForeignKey(e => e.GroupId)
            .OnDelete(DeleteBehavior.Restrict);
}

public DbSet<Article> Articles { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<GroupUser> GroupUsers { get; set; }
public DbSet<ArticleGroup> ArticleGroups { get; set; }

Open in new window


ArticleRepository.cs

public bool UpdateArticle(Article article)
{
        var articleGroups = _appDbContext.ArticleGroups.Where(a => a.ArticleID == article.ArticleID);
        _appDbContext.ArticleGroups.RemoveRange(articleGroups);
        _appDbContext.Articles.Update(article);
        return (_appDbContext.SaveChanges() >= 0);
}

Open in new window


I've seen similar problems with such complicated solutions that don't compare to something as trivial as this. Where in EF am I setting up incorrectly? I'm thinking its sql statements not executing in the right order?

I would think the game plan would be remove all from join table with the same articleID and insert the new values.

I've been using Microsoft docs to guide me on the Many 2 Many relationship here:

https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/complex-data-model?view=aspnetcore-2.2

https://docs.microsoft.com/en-us/ef/core/modeling/relationships
Avatar of John_Vidmar
John_Vidmar
Flag of Canada image

Yes, delete order is important, long answer:

To logically implement a many-to-many relationship between two entities, you need a third intersect entity.  Physically, each entity is a table, you'll have 3 tables:

tableA --> Intersect_table <-- tableB

* TableA has a one-to-many relationship with the Intersect_Table

*TableB has a one-to-many relationship with the Intersect_Table

All three tables have primary-keys, but the Intersect_Table also has foreign-key constraints to both TableA, and TableB.

If there is a row in the Intersect_Table that has a foreign-key to TableA, and you try to delete that value from TableA then the foreign-key constraint shall prevent that action because you would have an orphaned-key (database integrity is lost).  Also, several other tables may have a foreign-key constraint back to TableA.  If you want to delete a row from TableA then you must first delete all rows, from any table holding the matching foreign-key value prior to issuing the delete against TableA:

delete Table1 where TableA_key = 10
delete Table2 where TableA_key = 10
delete Table3 where TableA_key = 10
...
delete TableN where TableA_key = 10
delete Intersect_table where TableA_key = 10
delete TableA where TableA_key = 10  -- this delete must be last
Having a many-to-many relationship is one of the rare cases, where using a CASCADE DELETE in the database should be considered.

BUT - In the given sample, maybe more context would be necessary: Articles are never deleted during normal operations. They are only marked as no longer available (changing state).
Avatar of Marco Medina
Marco Medina

ASKER

How would one setup this using EF Core?

Right now I want to update the article, not delete it.

How would I setup Fluent and my Entities to get the article to update with the groups I am adding and removing?

ArticleGroups is the table being updated and causing the exception being thrown.

I think Article is fine in this scenario where the updates to the properties are irrelevant.

The Groups should still exist after update because they can be assigned to another article at any time.
This question needs an answer!
Become an EE member today
7 DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform.
View membership options
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.