Search code examples
c#entity-frameworkef-code-firstrelationshiptable-relationships

how to define many-to-many and one-to-many relations between two entities in code first?


Im creating a web-site using entity-framework code-first where a: user can sell many products: one-to-many. user can buy many products, and products can be bought by many users: many-to-many. i keep getting the error:

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

i do want the cascade delete in order to delete products that belong to a user that deleted his account, or delete records from the UserProduct table that belong to a deleted user, or deleted product. what am i doing wrong?

this is the User class:

public class User
{
    public int UserId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
    public string Email { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }


    public ICollection<Product> Products { get; set; }
    [InverseProperty("Users")]
    public ICollection<Product> Cart { get; set; }

    public User()
    {
        Products = new List<Product>();
        Cart = new List<Product>();
    }
}

this is the Product class:

public class Product
{
    [Key]
    public int ProductId { get; set; }
    public int OwnerId { get; set; }
    public string Title { get; set; }
    public string ShortDescription { get; set; }
    public string LongDescription { get; set; }

    public byte[] Picture1 { get; set; }
    public byte[] Picture2 { get; set; }
    public byte[] Picture3 { get; set; }

    [ForeignKey("OwnerId")]
    public User User { get; set; }
    public ICollection<User> Users { get; set; }

    public Product()
    {
        Users = new List<User>();
    }
}

Solution

  • first i needed to write a class for the middle table in the many-to-many relationship users-to-products:

    public class UserProduct
    {
        [Key, Column(Order = 0), ForeignKey("User")]
        public int UserId { get; set; }
        [Key, Column(Order = 1), ForeignKey("Product")]
        public int ProductId { get; set; }
    
        public User User { get; set; }
        public Product Product { get; set; }
    }
    

    that allow me to disable the cascade on delete from this table to the User table (overriding the OnModelCreating in the context class):

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
    
            modelBuilder.Entity<UserProduct>().HasRequired(t => 
            t.User).WithMany(t => t.Cart).WillCascadeOnDelete(false);
        }
    

    disabling the cascade works because:

    1. it solves the multiple cascade path error.
    2. deleting a user still deletes hes products and theirs UserProducts, and deleting a product will delete its UserProducts.