Home > front end >  EF Core Many to Many Querying to join 3 tables
EF Core Many to Many Querying to join 3 tables

Time:11-17

I have the following Code:

public class ProductTbl
{
    public override int Id { get; set; }
    public string ProductName { get; set; }
    public List<ProductManufacturer> ProductManufacturer { get; set; } //M2M
}

public class Manufacturer_LKP
{
    public override int Id { get; set; }
    public string ManufacturerName { get; set; }
    public List<ProductManufacturer> ProductManufacturer { get; set; } //M2M
}

public class ProductManufacturer
{
    public ProductTbl Product { get; set; }
    public int ProductID { get; set; }
    public Manufacturer_LKP Manufacturer { get; set; }
    public int ManufacturerID { get; set; }
}
  public class SupplierTbl
{
    public  int SupplierID { get; set; }
    public string SupplierName { get; set; }

}

public class ProductSuppliertbl 
{
    public  int Id { get; set; }
    public ProductTbl Product { get; set; }
    public int ProductID { get; set; }
    public SuppilerTbl Supplier { get; set; }
    public int SupplierID { get; set; }
}

*I need to write Linq query to join all 3 tables (Product,Manufacture,ProductManufacturer) to get ProductName and ManufatureName together in one DB trip
*When I do the following I missed the Manufacture object (Manufacture=Null)

DbSet<ProductTbl>()
    .Where(a => a.Id == 5)
    .AsNoTracking()
    .Include(a => a.ProductType)
    .Include(a => a.ProductManufacturer)

Above Linq Just joint Product table with ProductManufacture Table So I cannot Get "ManufactureName"

So Is there is any way to join the 3 tables to get ManufactureName beside the ProductName in one DB trip?

CodePudding user response:

Projection is your friend when trying to load related data. The issue with many-to-many is that you are saying a product has many manufacturers, while at the same time it has many suppliers

The Product would need a reference to the ProductSuppliers for that product to easily manage the many suppliers requirement.

var productData = context.Products
    .Select(p => new 
    {
        p.ProductName,
        ManufacturerNames = p.ProductManufacturers.Select(pm => pm.Manufacturer.ManufacturerName).ToList(),
        SupplierNames = x.ProductSuppliers.Select(ps => ps.Supplier.SupplierName).ToList()
    }).ToList();

This gives you a list of products, with each product's associated manufacturer names and supplier names. With that data you can format output how you see fit.

If you want the entities themselves, then the missing bit is ThenInclude:

var products = context.Products
    .Include(p => p.ProductManufacturers)
    .ThenInclude(pm => pm.Manufacturer)
    .Include(p => p.ProductSuppliers)
    .ThenInclude(ps => ps.Supplier)
    .AsNoTracking()
    .ToList();

This would load the entire entity graph.

If you don't want or cannot put a ProductSuppliers collection in product then you can build the query entirely from the ProductSupplier, but it's a bit messier.

If you are using EF Core 5 and your joining entities (ProductManufacturer/ProductSupplier) are just simply the FK references to their respective entities, then you can do away with the joining entity and let EF manage it behind the scenes. Product would just contain a collection of Manufacturers and a collection of Suppliers. These can be configured still with a HasMany..WithMany, but makes queries a lot cleaner to look at without the intermediate entities.

I.e.

var productData = context.Products
    .Select(p => new 
    {
        p.ProductName,
        ManufacturerNames = p.Manufacturers.Select(m => m.ManufacturerName).ToList(),
        SupplierNames = x.Suppliers.Select(s => ps.SupplierName).ToList()
    }).ToList();

and

var products = context.Products
    .Include(p => p.Manufacturers)
    .Include(p => p.Suppliers)
    .AsNoTracking()
    .ToList();

... respectively. Intermediate joining entities are only needed if there are additional properties you want to access in the joining entity. (I.e. CreatedBy/At, etc.)

CodePudding user response:

try this

var list = context.ProductManufactures
.Select(i => new
{
    ProductName = i.Product.ProductName,
    ManufacturerName = i.Manufacturer.ManufacturerName,
    SupplierNames = i.Product.ProductSuppliers.Select(ps => ps.Supplier.SupplierName)
}).ToList();

and fix some navigation properties

public class Product
{
    public  int Id { get; set; }
    public string ProductName { get; set; }
    public List<ProductManufacturer> ProductManufacturers { get; set; }
    public List<ProductSupplier> ProductSuppliers { get; set; } 
}

public class Supplier
{
    public int SupplierID { get; set; }
    public string SupplierName { get; set; }
   public List<ProductSupplier> ProductSuppliers { get; set; }
}

public class ProductSupplier
{
    public int Id { get; set; }
    public Product Product { get; set; }
    public int ProductID { get; set; }
    public Supplier Supplier { get; set; }
    public int SupplierID { get; set; }
}

public class Manufacturer_LKP
{
    public  int Id { get; set; }
    public string ManufacturerName { get; set; }
    public List<ProductManufacturer> ProductManufacturer { get; set; } 
}

public class ProductManufacturer
{
    public Product Product { get; set; }
    public int ProductID { get; set; }
    public Manufacturer_LKP Manufacturer { get; set; }
    public int ManufacturerID { get; set; }
}
  • Related