0
votes

I create a webshop with .Net Core and I'm using Entity Framework code-first migration.

I try to implement a product-order, many-to-many connection and I got the following error:

Unable to determine the relationship represented by navigation property 'Order.Products' of type 'ICollection'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'

My implementations are:

Order.cs

public class Order 
{
    [Key]
    public int OrderId { get; set; }
    ...
    public virtual ICollection<Product> Products { get; set; }

}

Product.cs

public class Product
{
    [Key]
    public int ProductId { get; set; }
    ...
    public virtual ICollection<Order> Orders { get; set; }
}

OrderProducts.cs

public class OrderProduct
{
    [Key]
    public int Id { get; set; }

    [ForeignKey("Order")]
    public int OrderId { get; set; }

    [ForeignKey("Product")]
    public int ProductId { get; set; }

    public virtual Order Order { get; set; }
    public virtual Product Product { get; set; }
}

MyContext:

public class WebShopContext : IdentityDbContext
{
    public WebShopContext(DbContextOptions<WebShopContext> options)
        : base(options)
    {
    }

    public DbSet<Product> Product { get; set; }
    public DbSet<Order> Order { get; set; }
    public DbSet<OrderProduct> OrderProduct { get; set; }
}

What am I doing wrong?

3

3 Answers

1
votes

I would make a few changes for many-to-many relationships.

1) Point to the mapping table from the other Product and Order

Order.cs

public class Order 
{
    [Key]
    public int OrderId { get; set; }
    ...
    public virtual ICollection<OrderProduct> Products { get; set; }

}

Product.cs

public class Product
{
    [Key]
    public int ProductId { get; set; }
    ...
    public virtual ICollection<OrderProduct> Orders { get; set; }
}

Personally I usually name the mapping table something like ProductInOrder or ProductOrderMapping to make it a bit more clear that it's a mapping.

I would also use a composite key on the mapping entity so that the OrderId and ProductId together becomes the primary key. You remove the Id from OrderProduct and then do what Ali Dogan said

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
  modelBuilder.Entity<OrderProduct>().HasKey(sc => new { sc.OrderId, sc.ProductId});});
}

The one thing to consider here is the order of the keys. If you are ever going going to query into this table based on one of the keys much more than the other you want that to be first for index reasons.

1
votes

Add to WebShopContext

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
  modelBuilder.Entity<OrderProduct>().HasKey(sc => new { sc.OrderId, sc.ProductId});});
}