0

I'm currently learning ASP.NET MVC and Web API.

I'm trying to create a User Model. Users can have any number of UserContacts. UserContacts reference the User it is a contact of and the User who is the contact. I have made a model called UserContact because attached to this Model is additional information.

public class User
{
    public int UserID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class UserContact
{
    public int UserContactID { get; set; }

    public int UserID { get; set; }
    [ForeignKey("UserID"), Column(Order = 0)]
    [Required]
    public User User { get; set; }

    public int ContactID { get; set; }
    [ForeignKey("ContactID"), Column(Order = 1)]
    [Required]
    public User Contact { get; set; }

    public DateTime ContactSince { get; set; }
}

So this gives me an error referring to cascading Delete. How do I set up a relationship like this where two foreign keys point to the same Model type? I have yet to grasp Entity Framework syntax as well. If I don't have an ICollection of UserContacts in the User model, does this hinder my ability to grab the UserContacts associated with that User?

Jeff Becker
  • 125
  • 2
  • 8

2 Answers2

1

When you have a foreign key and the foreign key columns are not nullable(means,required). EF will automatically tries to enable cascading delete on the relationsip. In your case, it will try to enable Cascading delete for both the foreign key columns and both of them points to the same user table! That is the reason you are getting this error. What if you have a UserContact record with Both UserId and ContactID points to the same User record. Cascading delete is confused now :)

Also, since one user can have more than one Contacts, We need a Contacts property on the User table to represent that. This will be a collection of UserContact's. Also this user can be a a contact of many other people. So Let's create another property for that.

public class User
{
    public int UserID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public ICollection<UserContact> Contacts { set; get; } 
    public ICollection<UserContact> ContactOf { set; get; }
}

public class UserContact
{
    public int UserContactID { get; set; }

    public int UserID { get; set; }
    public User User { get; set; }

    public int ContactID { get; set; }
    public User Contact { get; set; }

    public DateTime ContactSince { get; set; }
}

And in your DbContext class, We can configure the foreign key relation ships and tell EF to disable cascade delete using fluent configuration inside the overridden OnModelCreating method. The below code will disable cascading delete on both the the relationships. But for your error to go away. disabling on one foreign key is enough.

public class YourDbContext: DbContext
{       
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<UserContact>()
          .HasRequired<User>(g=>g.User)
          .WithMany(g=>g.Contacts)
          .HasForeignKey(g=>g.UserID)
          .WillCascadeOnDelete(false);

        modelBuilder.Entity<UserContact>()
          .HasRequired<User>(g => g.Contact)

          .WithMany(g => g.ContactOf)
          .HasForeignKey(g => g.ContactID)
          .WillCascadeOnDelete(false); // this one is not really needed to fix the error

        base.OnModelCreating(modelBuilder);
    }

    public DbSet<User> Users { set; get; } 
    public DbSet<UserContact> UserContacts { set; get; } 
}

This will create the tables like you wanted with the necessary foreign keys. enter image description here

Shyju
  • 214,206
  • 104
  • 411
  • 497
0

There is not enough information for EF to figure out the relationships on the other side, so yes, you need collections. You can use the InverseProperty annotation to clarify (or fluent api statements):

public class User
{
    public int UserID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    [InverseProperty("User")]
    public Virtual ICollection<UserContact> Users{ get; set; }
    [InverseProperty("Contact")]
    public Virtual ICollection<UserContact> Contacts { get; set; }
}

public class UserContact
{
    public int UserContactID { get; set; }

    public int UserID { get; set; }
    [ForeignKey("UserID"), Column(Order = 0)]
    [Required]
    public User User { get; set; }

    public int ContactID { get; set; }
    [ForeignKey("ContactID"), Column(Order = 1)]
    [Required]
    public User Contact { get; set; }

    public DateTime ContactSince { get; set; }
}

http://www.entityframeworktutorial.net/code-first/inverseproperty-dataannotations-attribute-in-code-first.aspx

Steve Greene
  • 12,029
  • 1
  • 33
  • 54
  • Still receiving this error: Introducing FOREIGN KEY constraint 'FK_dbo.UserContacts_dbo.Users_ContactID' on table 'UserContacts' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. Could not create constraint. See previous errors. – Jeff Becker Jan 09 '16 at 17:42
  • What code is generating the error? Is it a true referential integrity error where you're deleting a parent without removing the children? Also be aware cascading deletes in EF is not the same as SQL Server cascading deletes. – Steve Greene Jan 09 '16 at 17:45
  • The error is generated when I use Update-Database in the Package Manager Console – Jeff Becker Jan 09 '16 at 17:46
  • See Shyju's fluent code where cascade delete is off. – Steve Greene Jan 09 '16 at 17:46
  • More info: http://stackoverflow.com/questions/19373310/introducing-foreign-key-constraint-may-cause-cycles-or-multiple-cascade-paths – Steve Greene Jan 09 '16 at 17:50