Home > Software engineering >  Why is entity framework trying to delete the associated row on delete?
Why is entity framework trying to delete the associated row on delete?

Time:11-14

So I've got this code:

...
     modelBuilder.Entity<Person>(builder => {
           builder.ToTable("person");
           builder.Property(x => x.PersonId).HasColumnName("nconst");
           builder.Property(x => x.FirstName).HasColumnName("firstname");
           builder.Property(x => x.LastName).HasColumnName("lastname");
           builder.Property(x => x.BirthYear).HasColumnName("birthyear");
           builder.HasKey(x => x.PersonId);
           builder.HasMany(x => x.Works).WithOne().HasForeignKey(x => x.PersonId);
           builder.HasOne(x => x.Death).WithOne().HasForeignKey<Death>(x => x.PersonId);
     });

     modelBuilder.Entity<PersonBookmark>(builder => {
          builder.ToTable("personbookmark");
          builder.Property(x => x.PersonId).HasColumnName("nconst");
          builder.Property(x => x.UserId).HasColumnName("user_id");
          builder.Property(x => x.Date).HasColumnName("date");
          builder.Property(x => x.Label).HasColumnName("label");
          builder.HasKey(x => new { x.UserId, x.PersonId });
          builder.HasOne(x => x.User).WithMany(x => x.PersonBookmarks).HasForeignKey(x => x.UserId);
          builder.HasOne(x => x.Person).WithOne().HasPrincipalKey<PersonBookmark>(x => x.PersonId).HasForeignKey<Person>(x => x.PersonId);
     });
...
namespace DataService.Domain {

    public class PersonBookmark {

        public int UserId { get; set; }

        public DateTime Date { get; set; }

        [StringLength(100, MinimumLength = 1)]
        public string Label { get; set; }

        [StringLength(10, MinimumLength = 10)]
        public string PersonId { get; set; }


        [NotMapped]
        public virtual User User { get; set; }

        [NotMapped]
        public virtual Person Person { get; set; }
    }
}
namespace DataService.Domain {

    public class Person {

        public Person() {
            Works = new HashSet<Work>();
        }

        [StringLength(10, MinimumLength = 10)]
        public string PersonId { get; set; }

        [StringLength(100, MinimumLength = 1)]
        public string LastName { get; set; }

        [StringLength(100, MinimumLength = 1)]
        public string FirstName { get; set; }

        [StringLength(4, MinimumLength = 4)]
        public string BirthYear { get; set; }


        [NotMapped]
        public virtual ICollection<Work> Works { get; set; }

        [NotMapped]
        public virtual Death Death { get; set; }
    }
}
public bool DeletePersonBookmark(int UserId, string PersonId) {
    var personBookmark = Context.PersonBookmarks.FirstOrDefault(x => x.UserId == UserId && x.PersonId == PersonId);
     var found = personBookmark != null;

     if (found) {
          Context.PersonBookmarks.Remove(personBookmark);
          Context.SaveChanges();
     }

     return found;
}

and when I'm trying to remove a PersonBookmark from the database, I've got this Exception:

Microsoft.EntityFrameworkCore.DbUpdateException : An error occurred while updating the entries. See the inner exception for details.
---- Npgsql.PostgresException : 23503: update or delete on table "person" violates foreign key constraint "personbookmark_nconst_fkey" on table "personbookmark"

It looks like entity framework is trying to remove the associated Person row, but I don't know where I told him to do this... I just want the PersonBookmark row to be deleted. Maybe I'm not understanding something obvious but as I view things the Person shadow property does not mean the associated Person should disappear with its reference owner.

Can someone help me?

Thank you in advance

CodePudding user response:

It looks like entity framework is trying to remove the associated Person row, but I don't know where I told him to do this...

Right here

builder.HasOne(x => x.Person).WithOne()
   .HasPrincipalKey<PersonBookmark>(x => x.PersonId) // <--
   .HasForeignKey<Person>(x => x.PersonId);

For one-to-one relationships the generic type argument of HasPrincipalKey / HasForeignKey is used to specify who is the principal and who is the dependent in the relationship (docs). And cascade delete works from principal to dependent direction, i.e. deleting principal cascade deletes dependent(s).

What you need here is to exchange Person and PersonBookmark roles:

builder.HasOne(x => x.Person).WithOne()
   .HasPrincipalKey<Person>(x => x.PersonId)
   .HasForeignKey<PersonBookmark>(x => x.PersonId);

Make sure to regenerate the migration and apply it to the database.

  • Related