Entity Framework (EF) Code First Cascade Delete for One-to-Zero-or-One relationship

113,306

Solution 1

You will have to use the fluent API to do this.

Try adding the following to your DbContext:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{   
    modelBuilder.Entity<User>()
        .HasOptional(a => a.UserDetail)
        .WithOptionalDependent()
        .WillCascadeOnDelete(true);
}

Solution 2

You could also disable the cascade delete convention in global scope of your application by doing this:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>()
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>()

Solution 3

This code worked for me

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<UserDetail>()
            .HasRequired(d => d.User)
            .WithOptional(u => u.UserDetail)
            .WillCascadeOnDelete(true);
    }

The migration code was:

public override void Up()
    {
        AddForeignKey("UserDetail", "UserId", "User", "UserId", cascadeDelete: true);
    }

And it worked fine. When I first used

modelBuilder.Entity<User>()
    .HasOptional(a => a.UserDetail)
    .WithOptionalDependent()
    .WillCascadeOnDelete(true);

The migration code was:

AddForeignKey("User", "UserDetail_UserId", "UserDetail", "UserId", cascadeDelete: true); 

but it does not match any of the two overloads available (in EntityFramework 6)

Share:
113,306
arsenalogy
Author by

arsenalogy

Updated on July 11, 2020

Comments

  • arsenalogy
    arsenalogy almost 4 years

    Following the "Code First Modeling" section of the Pluralsight "Getting Started with Entity Framework 5" course by Julie Lerman, I created two POCO classes with a one-to-zero-or-one relationship: a parent (User) and an optional child (UserDetail).

    User and UserDetail data model diagram (click to view).

    Notice in the diagram that the UserId property is a primary key and a foreign key for UserDetail.

    Relevant code:

    public class User
    {
        //...
    
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int UserId { get; set; }
    
        /* Has a 1:0..1 relationship with UserDetail */
        public virtual UserDetail UserDetail { get; set; }
    
        //...
    }
    
    public class UserDetail
    {
        //...
    
        /* Has a 0..1:1 relationship with User */
        public virtual User User { get; set; }
    
        [Key, ForeignKey("User")]
        public int UserId { get; set; }
    
        //...
    }
    
    public class EFDbContext : DbContext
    {
        public DbSet<User> Users { get; set; }
        //public DbSet<UserDetail> UserDetails { get; set; }  /* Explicit declaration not necessary. Context is aware of UserDetail entity due to 0..1:1 relationship with User */
    
        public EFDbContext()
        {
            Configuration.ProxyCreationEnabled = true;
            Configuration.LazyLoadingEnabled = true;
        }
    }
    
    public class UserRepository : IUserRepository
    {
        private EFDbContext _context = new EFDbContext();
    
        public void Delete(User entity)
        {
            entity = _context.Users.Find(entity.UserId);
    
            //...
    
            _context.Users.Remove(entity);
            _context.SaveChanges();
    
            //...
        }
    }
    

    When the Delete() method in the UserRepository class is called, it does not delete the User record in the database because the foreign key in UserDetail does not have cascade delete enabled.

    The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.UserDetail_dbo.User_UserId".

    How would you enable cascading deletes for one-to-zero-or-one relationships using Entity Framework Code First (so that deleting a User automatically deletes UserDetail)?