Search code examples
c#entity-framework-coreasp.net-core-2.1

Entity Framework core 2.1 Many To Many Select Query


How would I create the following query using Linq?

SELECT product.name, product.code, category.Name FROM product 
INNER JOIN productCategories ON product.ID = productCategories.productID 
INNER JOIN category ON productCategories.categoryID = category.ID 
WHERE productCategories.ID = idToFind

Product & Category Classes:

public class Product
{
    public Product()
    {
        this.Categories = new HashSet<Category>();
    }    
    public int ID { get; set; }    
    public string Code { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Category> Categories { get; set; }
}


public class Category
{
    public Category()
    {
        this.Products = new HashSet<Product>();
        this.Children = new HashSet<Category>();
    }
    public int ID { get; set; }

    [StringLength(150)]
    public string Name { get; set; }
    public int? ParentID { get; set; }
    public virtual Category Parent { get; set; }

    public virtual ICollection<Category> Children { get; set; }
    public virtual ICollection<Product> Products { get; set; }
}

I have tried a few different things and can get the results if I only need columns from one table, but can not get details from both tables i.e. category name & product name.

Edit: I have now added a JunctionClass

public class CategoryProduct
{
    public int CategoryID { get; set; }
    public Category Category { get; set; }

    public int ProductID { get; set; }
    public Product Product { get; set; }

}

and tried :

var results = _context.Product.Include(e => e.categoryProducts).ThenInclude(e => e.Category).Where(c=>c.categoryProducts.Category.ID==169).ToList();

But I still cant make it work. Getting the error:

'ICollection<CategoryProduct>' does not contain a definition for 'Category' and no accessible extension method 'Category' accepting a first argument of type 'ICollection<CategoryProduct>' could be found 

Solution

  • In EF core, you need a junction table to map many-to-many relationships.

    public class ProductCategory
    {
        public int Id { get; set; }
        public int ProductId { get; set; }
        public Product Product { get; set; }
        public int CategoryId { get; set; }
        public Category Category { get; set; }
    }
    
    public class Product
    {
        ...
        public virtual ICollection<ProductCategory> ProductCategories { get; set; }
    }
    
    public class Category
    {
        ...
        public virtual ICollection<ProductCategory> ProductCategories { get; set; }
    }
    
    // DbContext
    public DbSet<ProductCategory> ProductCategories { get; set; }
    
    public override OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<ProductCategory>()
               .HasOne(pc => pc.Product)
               .WithMany(p => p.ProductCategories);
    
        builder.Entity<ProductCategory>()
               .HasOne(pc => pc.Category)
               .WithMany(c => c.ProductCategories);
    }
    
    // Query
    var result = await dbContext.ProductCategories
                         .Select(pc => new {
                             ProductName = pc.Product.Name, 
                             ProductCode = pc.Product.Code, 
                             CategoryName = pc.Category.Name 
                          })
                         .SingleOrDefaultAsync(pc => pc.Id == idToFind)