EF Core多对多查询加入3个表

Posted

技术标签:

【中文标题】EF Core多对多查询加入3个表【英文标题】:EF Core Many to Many Querying to join 3 tables 【发布时间】:2021-12-27 19:33:54 【问题描述】:

我有以下代码:

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; 

*我需要编写 Linq 查询来连接所有 3 个表(Product、Manufacture、ProductManufacturer),以便在一次 DB 行程中同时获得 ProductName 和 ManufatureName *当我执行以下操作时,我错过了 Manufacture 对象 (Manufacture=Null)

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

以上 Linq 只是将 Product 表与 ProductManufacture 表联合起来,所以我无法获取“ManufactureName”

那么有什么方法可以在一次 DB 行程中加入 3 个表以在 ProductName 旁边获取 ManufactureName 吗?

【问题讨论】:

那么,最后需要什么?具有两个字段或整个对象图的 DTO? Include不是JOIN,是加载相关数据的指令。 您不需要任何类型的 JOIN。 Include 不加入,它控制急切加载。 EF Core 根据实体之间的关系根据需要生成 JOIN。如果您的查询实际检索到相关的实体属性,即使没有Include,EF 也会生成 JOIN 【参考方案1】:

在尝试加载相关数据时,投影是您的朋友。多对多的问题是你说一个产品有很多制造商,同时它有很多供应商

该产品需要对该产品的 ProductSuppliers 的引用,以便轻松管理许多供应商的要求。

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();

这会为您提供一个产品列表,其中包含每个产品的相关制造商名称和供应商名称。使用该数据,您可以按照您认为合适的方式格式化输出。

如果你想要实体本身,那么缺少的位是ThenInclude

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

这将加载整个实体图。

如果您不想或不能将 ProductSuppliers 集合放入产品中,那么您可以完全从 ProductSupplier 构建查询,但这有点麻烦。

如果您使用的是 EF Core 5 并且您的加入实体(ProductManufacturer/ProductSupplier)只是对其各自实体的 FK 引用,那么您可以取消加入实体并让 EF 在幕后对其进行管理。产品将只包含一组制造商和一组供应商。这些仍然可以使用HasMany..WithMany 进行配置,但是在没有中间实体的情况下使查询更加清晰。

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();

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

...分别。仅当您要在连接实体中访问其他属性时,才需要中间连接实体。 (即 CreatedBy/At 等)

【讨论】:

【参考方案2】:

试试这个

var list = context.ProductManufactures
.Select(i => new

    ProductName = i.Product.ProductName,
    ManufacturerName = i.Manufacturer.ManufacturerName,
    SupplierNames = i.Product.ProductSuppliers.Select(s => s.SupplierName).ToList()
).ToList();

或者你也可以试试这个

var productData = context.Products
    .Select(i => new 
    
        ProductName= i.ProductName,
        ManufacturerNames = i.ProductManufacturers.Select(m => m.Manufacturer.ManufacturerName),
        SupplierNames = i.ProductSuppliers.Select(s => s.Supplier.SupSupplierName)
    ).ToList();

但在此之前您必须修复一些导航属性

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; 

【讨论】:

请格式化您的代码,阅读起来很糟糕。 @Serge 知道了,但是如果我有另一个与产品表有多对多关系的表怎么办。即供应商_TBL,我需要在同一个数据库行程中的(产品名称和制造商名称)旁边附加供应商名称??感谢您的帮助:) 请也发布您的另一个表以及它与 3 个表的关系 公共类 SupplierTbl public int SupplierID get;放; 公共字符串供应商名称 获取;放; 公共类 ProductSupplierTbl 公共 int Id 获取;放; 公共 ProductTbl 产品 获取;放; 公共 int ProductID 获取;放; 公共 SuppilerTbl 供应商 获取;放; 公共 int 供应商 ID 获取;放; @Serge 主帖已更新为新实体“供应商”。现在我需要得到 (Product_Name , Manufacturer_Name, Supplier_Name) @Serge

以上是关于EF Core多对多查询加入3个表的主要内容,如果未能解决你的问题,请参考以下文章

EF Core 多对多关系上的 LINQ 查询

如何在 EF Core 中查询多对多关系

EntityFramework同一张表多对多关系

Django:表多对多查询聚合分组FQ查询事务

多对多 EF Core 已被跟踪 - C# Discord Bot

EF Core:LINQ 选择“多对多”到“多对多”