59

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)?

arsenalogy
  • 601
  • 1
  • 5
  • 5

3 Answers3

77

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);
}
Jeroen
  • 60,696
  • 40
  • 206
  • 339
hutchonoid
  • 32,982
  • 15
  • 99
  • 104
  • 1
    The WillCascadeOnDelete() is what did it, although I had to apply it on the dependent table (UserDetail), not the principal (User) in this relationship.Also, I removed Key and ForeignKey data annocations on UserId property in the UserDetail class. Thank you so much! – arsenalogy Jul 06 '13 at 02:10
  • 6
    FWIW, this article helped me get the idea of applying the WillCascadeOnDelete() to the dependent table http://msdn.microsoft.com/en-us/data/jj591620.aspx#RequiredToOptional. – arsenalogy Jul 06 '13 at 02:18
  • 6
    Exactly what I needed. Some have suggested [Required] for cascading delete. This indeed works, but of course, only if it is actually required. –  Oct 18 '13 at 23:26
  • 25
    There is no way to do this with annotation instead of fluent API? – Rosdi Kasim Apr 14 '14 at 07:55
  • 4
    @Rosdi Did you read the comment right above yours? You can use the [Required] attribute if you don't mind the fk not being nullable. – Ryan Buddicom Apr 28 '15 at 06:47
  • So, If I got this right from CodeMonkey's comment, if we have a relation with required set to the relation, then cascading delete is performed automatically, is this correct? – Nick Louloudakis May 04 '15 at 14:40
  • I just tried it using the [Required] attribute, doesn't seem to work in EF6.x - in general, that attribute does not seem to have any effect on the created schema. – Efrain Aug 23 '16 at 14:01
  • Could also do it in the configuration file. `HasOptional( en => en.Tags ) .WithOptionalDependent() .WillCascadeOnDelete(true);` – neilhighley Sep 06 '17 at 10:14
6

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>()
rajeemcariazo
  • 2,476
  • 5
  • 36
  • 62
4

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)

Lupa
  • 586
  • 1
  • 8
  • 22