简体   繁体   中英

How to get cascade delete to work with my blog project?

I have a simple project with a blog format. There are users and blogs. The user id is present as a foreign key in the blog table. SQL Creation query as follows:

CREATE TABLE BlogUser (
    BlogUser_ID int NOT NULL IDENTITY (1,1) CONSTRAINT PK_User PRIMARY KEY CLUSTERED,
    DisplayName varchar(50) NOT NULL);

CREATE TABLE BlogPost (
    Post_ID int NOT NULL IDENTITY (1,1) CONSTRAINT PK_Blog PRIMARY KEY CLUSTERED,
    BlogUser_ID int NOT NULL CONSTRAINT FK_BlogPost_BlogUser REFERENCES BlogUser(BlogUser_ID),
    BlogTitle varchar(30) NOT NULL,
    BlogDate datetime NOT NULL,
    BlogContent varchar(8000) NOT NULL);

The following are the models:

    public partial class BlogPost
    {
        public int PostId { get; set; }
        public int BlogUserId { get; set; }
        public string BlogTitle { get; set; }
        public DateTime BlogDate { get; set; }
        public string BlogContent { get; set; }

        public virtual BlogUser BlogUser { get; set; }
    }
    public partial class BlogUser
    {
        public BlogUser()
        {
            BlogPost = new HashSet<BlogPost>();
        }

        public int BlogUserId { get; set; }
        public string DisplayName { get; set; }

        public virtual ICollection<BlogPost> BlogPost { get; set; }
    }

These are the Models in the DbContext Class:

protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //Blog Post
            modelBuilder.Entity<BlogPost>(entity =>
            {
                entity.HasKey(e => e.PostId).HasName("PK_Blog");

                entity.Property(e => e.PostId).HasColumnName("Post_ID");

                entity.Property(e => e.BlogContent)
                    .IsRequired()
                    .HasMaxLength(8000)
                    .IsUnicode(false);

                entity.Property(e => e.BlogDate).HasColumnType("datetime");

                entity.Property(e => e.BlogTitle)
                    .IsRequired()
                    .HasMaxLength(30)
                    .IsUnicode(false);

                entity.Property(e => e.BlogUserId).HasColumnName("BlogUser_ID");

                entity.HasOne(d => d.BlogUser)
                    .WithMany(p => p.BlogPost)
                    .HasForeignKey(d => d.BlogUserId)
                    .OnDelete(DeleteBehavior.Cascade) //--> What is missing here?
                    .HasConstraintName("FK_BlogPost_BlogUser");
            });

            //Blog User
            modelBuilder.Entity<BlogUser>(entity =>
            {
                entity.Property(e => e.BlogUserId).HasColumnName("BlogUser_ID");

                entity.Property(e => e.DisplayName)
                    .IsRequired()
                    .HasMaxLength(50)
                    .IsUnicode(false);
            });

            OnModelCreatingPartial(modelBuilder);
        }

If I delete a user who has posted blogs, I would like to have all the blogs they posted deleted as well without having to write a script to remove their blogs one by one.

As you can see, I've placed the OnDelete(DeleteBehavior.Cascade) on the foreign key that links the two. But when I try to delete a user with blogs, I receive an error stating 'The DELETE statement conflicted with the REFERENCE constraint "FK_BlogPost_BlogUser"'.

What am I missing?

This is actually for a larger project where there are upwards of 30 dependencies for several tables. The cascade behaviour is preferred for that part of the project, so its just logical to ask .NET to Cascade instead of writing 30 separate loops for each. Ideally the cascade behaviour should be applied only to the models in question and not the project as a whole.

Adding OnDelete(DeleteBehavior.Cascade) is not enough - you have to reflect that change in the database (as it can be seen, the existing FK constraint has no ON DELETE CASCADE option) - either manually (since the structure of the code indicates entity model scaffolded from existing database), or by generating an applying migration.

Btw, cascade delete is the default behavior for required relationships like shown, so it would have been set automatically by EF Core if you used code first and migrations workflow. However the designers of your existing database decided to not turn it on, so you have to either live with that (and remove OnDelete(Cascade) because it should reflect the database state) or modify it using the aforementioned methods. Whatever you decide, you have to keep EF Core metadata and physical database structure in sync - with few exceptions, many EF Core behaviors rely on database, so it is cooperative effort.

For more info, seeCascade Delete section of the EF Core official documentation

While configuring your model, just decide whether you need Cascade delete or not, for example:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Book>()
        .HasOptional<Author>(b => b.Author)
        .WithMany()
        .WillCascadeOnDelete(false);
}

I also use OnDelete(DeleteBehavior.Cascade) to delete all. Did you use Include(x => x.BlogPost)?

var blogUser = context.BlogUser.Where(x => x.BlogUserId == userId).Include(x => x.BlogPost).FirstOrDefault();
context.BlogUser.Remove(blogUser);
context.SaveChanges();

But if yours cannot work, then you can try this alternative method. Though it has 3 lines, at least it works.

var blogPosts = context.BlogPost.Where(x => x.BlogUserId == blogUser.BlogUserId).ToList();
context.BlogPost.RemoveRange(blogPosts);
context.BlogUser.Remove(blogUser);
context.SaveChanges();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM