简体   繁体   中英

Entity Framework Core code first - 0..1 to many relationship and cascade delete

I'm trying to create a commenting system backed by Entity Framework Core where multiple entities of different type can have comments attached to them.

These are my entities. (In the real application there are about 7 in total with varying relationships but this is how it generally looks)

public class Comment : IEntityBase
{
    public int Id { get; set; }

    public int? FreezerId{ get; set; }
    public Freezer Freezer { get; set; }
    public int? BoxId{ get; set; }
    public Box Box{ get; set; }
    public string Author { get; set; }
    public DateTime CreatedAt { get; set; }
    public string Content { get; set; }
}

public class Freezer: IEntityBase
{
    public int Id { get; set; }

    public string Name { get; set; }
    public string Location { get; set; }
    public ICollection<Box> Boxes{ get; set; }
    public ICollection<Comment> Comments { get; set; }
}

public class Box: IEntityBase
{
    public int Id { get; set; }

    public Freezer Freezer{get; set;}
    public int FreezerId{get; set;}
    public string Data{ get; set; }
    public ICollection<Comment> Comments { get; set; }
}

I want the Comment entity to be attached to one Freezer or one Box, but not both at the same time.

I defined the relationship in the fluent API as the following:

 builder.Entity<Box>(boxBuilder=>
        {
            boxBuilder.HasOne(box=> box.Freezer)
                .WithMany(freezer => freezer.boxes)
                .HasForeignKey(box => box.FreezerId)
                .IsRequired()
                .OnDelete(DeleteBehavior.Cascade);

            boxBuilder.HasMany(box => box.Comments)
                .WithOne(comment => comment.Box)
                .HasForeignKey(comment => comment.BoxId)
                .OnDelete(DeleteBehavior.Cascade);
        });

 builder.Entity<Freezer>(freezerBuilder =>
        {
            freezerBuilder.HasMany(freezer=> freezer.Comments)
                .WithOne(comment => comment.Freezer)
                .HasForeignKey(comment => comment.FreezerId)
                .OnDelete(DeleteBehavior.Cascade);
        });

When I try to update the database to this model I get the following error:

System.Data.SqlClient.SqlException: Introducing FOREIGN KEY constraint 'FK_Comment_Boxes_BoxId' on table 'Comment' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.

I think the error comes from the Box and the Freezer property in the Comment class not being optional which would make this a 1 to many relationship instead of a 0..1 to many relationship which is what I want.

With Entity Framework 6 I would just use the .HasOptional() method, but this doesn't exist in Entity Framework Core

I think one way to solve this would be to just subclass the Comment class and create a unique comment class for each entity that can be commented on and move the foreign key and reference property to that subclass instead.

But it feels like I shouldn't have to do it this way.

You have to disable the cascade delete( DeleteBehavior.Restrict ) then it will works for you:

 modelBuilder.Entity<Box>(boxBuilder =>
        {
          boxBuilder.HasOne(box => box.Freezer)
              .WithMany(freezer => freezer.Boxes)
              .HasForeignKey(box => box.FreezerId)
              .IsRequired()
              .OnDelete(DeleteBehavior.Cascade);

          boxBuilder.HasMany(box => box.Comments)
              .WithOne(comment => comment.Box)
              .HasForeignKey(comment => comment.BoxId)
              .OnDelete(DeleteBehavior.Restrict);
        });

        modelBuilder.Entity<Freezer>(freezerBuilder =>
        {
          freezerBuilder.HasMany(freezer => freezer.Comments)
              .WithOne(comment => comment.Freezer)
              .HasForeignKey(comment => comment.FreezerId)
              .OnDelete(DeleteBehavior.Restrict);
        });
        base.OnModelCreating(modelBuilder);
      }

Usage:

using (var myConext = new MyDbContext())
      {
        myConext.Database.EnsureCreated();
        myConext.Boxes.Add(new Box() {Freezer =  new Freezer()});
        myConext.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