繁体   English   中英

如何从导航属性中查询 EF Core dbcontext 的特定属性?

[英]How to query EF Core dbcontext for specific attributes from navigation properties?

我试图获得最有效的查询来选择导航属性中的一些嵌套属性,以便 API 发送仅包含相关信息的响应。 到目前为止,我尝试了Include()ThenInclude()方法以及这篇文章的答案。

然而,这两种方法都产生了类似的查询并从数据库中检索了相同的列。 唯一明显的区别是 Include 方法将不需要的列中的数据加载到实体对象属性中,而Select()方法没有。 我正在使用图层模式,因此使用 null 类型对我来说不是一个选项,并且实体中的 nul 值不是问题,因为只有检索值最终加载到 dto 中,但仍然是查询效率问题遗迹。

我的问题是,是否有更好的方法从导航属性中查询单个列,如果没有,上述两种方法中的哪一种更好。

包括查询方法:

public async Task<IEnumerable<Orden>> GetDetailed()
{
    return await _context.Ordenes
                         .Include(o => o.Detalles)
                         .ThenInclude(od => od.ProductosAlmacenados)
                         .Include(o => o.Detalles)
                         .ThenInclude(od => od.Tela)
                         .Include(o => o.Detalles)
                         .ThenInclude(od => od.Color)
                         .ToListAsync();
}

Select()查询方法:

public async Task<IEnumerable<Orden>> GetDetailed()
{
    var ordenes = await _context.Ordenes
                                .Select(o => new Orden
        {
            Id = o.Id,
            Estatus = o.Estatus,
            FechaRegistro = o.FechaRegistro,
            FechaRequerida = o.FechaRequerida,
            Detalles = o.Detalles.Select(d => new OrdenDetalles
            {
                Color = new Color
                {
                    Nombre = d.Color.Nombre
                },
                Tela = new Tela
                {
                    Nombre = d.Tela.Nombre,
                },
                Cantidad = d.Cantidad,
                ProductosAlmacenados = d.ProductosAlmacenados
            }).ToList()
        }
        ).ToListAsync();

    return ordenes;
}

Select生成的带有输出的查询:

SELECT `o`.`ID`, `o`.`Estatus`, `o`.`Fecha_Registro`, `o`.`Fecha_Requerida`, `t0`.`Nombre`, `t0`.`Nombre0`, `t0`.`Cantidad`, `t0`.`Color_ID`, `t0`.`Orden_ID`, `t0`.`Tela_ID`, `t0`.`ID`, `t0`.`ID0`, `t0`.`Almacen_ID`, `t0`.`OrdenDetalles_Color_ID`, `t0`.`OrdenDetalles_Orden_ID`, `t0`.`OrdenDetalles_Tela_ID`, `t0`.`Cantidad0`
  FROM `orden` AS `o`
  LEFT JOIN (
      SELECT `c`.`Nombre`, `t`.`Nombre` AS `Nombre0`, `o0`.`Cantidad`, `o0`.`Color_ID`, `o0`.`Orden_ID`, `o0`.`Tela_ID`, `c`.`ID`, `t`.`ID` AS `ID0`, `a`.`Almacen_ID`, `a`.`OrdenDetalles_Color_ID`, `a`.`OrdenDetalles_Orden_ID`, `a`.`OrdenDetalles_Tela_ID`, `a`.`Cantidad` AS `Cantidad0`
      FROM `ordendetalles` AS `o0`
      INNER JOIN `color` AS `c` ON `o0`.`Color_ID` = `c`.`ID`
      INNER JOIN `tela` AS `t` ON `o0`.`Tela_ID` = `t`.`ID`
      LEFT JOIN `almacen_productos` AS `a` ON ((`o0`.`Color_ID` = `a`.`OrdenDetalles_Color_ID`) AND (`o0`.`Orden_ID` = `a`.`OrdenDetalles_Orden_ID`)) AND (`o0`.`Tela_ID` = `a`.`OrdenDetalles_Tela_ID`)
  ) AS `t0` ON `o`.`ID` = `t0`.`Orden_ID`
  ORDER BY `o`.`ID`, `t0`.`Color_ID`, `t0`.`Orden_ID`, `t0`.`Tela_ID`, `t0`.`ID`, `t0`.`ID0`, `t0`.`Almacen_ID`, `t0`.`OrdenDetalles_Color_ID`, `t0`.`OrdenDetalles_Orden_ID`

MySQlWorkbench 输出

Include()生成的查询:

SELECT `o`.`ID`, `o`.`Estatus`, `o`.`Fecha_Registro`, `o`.`Fecha_Requerida`, `t0`.`Color_ID`, `t0`.`Orden_ID`, `t0`.`Tela_ID`, `t0`.`Cantidad`, `t0`.`ID`, `t0`.`ID0`, `t0`.`Almacen_ID`, `t0`.`OrdenDetalles_Color_ID`, `t0`.`OrdenDetalles_Orden_ID`, `t0`.`OrdenDetalles_Tela_ID`, `t0`.`Cantidad0`, `t0`.`Nombre`, `t0`.`Nombre0`
  FROM `orden` AS `o`
  LEFT JOIN (
      SELECT `o0`.`Color_ID`, `o0`.`Orden_ID`, `o0`.`Tela_ID`, `o0`.`Cantidad`, `t`.`ID`, `c`.`ID` AS `ID0`, `a`.`Almacen_ID`, `a`.`OrdenDetalles_Color_ID`, `a`.`OrdenDetalles_Orden_ID`, `a`.`OrdenDetalles_Tela_ID`, `a`.`Cantidad` AS `Cantidad0`, `t`.`Nombre`, `c`.`Nombre` AS `Nombre0`
      FROM `ordendetalles` AS `o0`
      INNER JOIN `tela` AS `t` ON `o0`.`Tela_ID` = `t`.`ID`
      INNER JOIN `color` AS `c` ON `o0`.`Color_ID` = `c`.`ID`
      LEFT JOIN `almacen_productos` AS `a` ON ((`o0`.`Color_ID` = `a`.`OrdenDetalles_Color_ID`) AND (`o0`.`Orden_ID` = `a`.`OrdenDetalles_Orden_ID`)) AND (`o0`.`Tela_ID` = `a`.`OrdenDetalles_Tela_ID`)
  ) AS `t0` ON `o`.`ID` = `t0`.`Orden_ID`
  ORDER BY `o`.`ID`, `t0`.`Color_ID`, `t0`.`Orden_ID`, `t0`.`Tela_ID`, `t0`.`ID`, `t0`.`ID0`, `t0`.`Almacen_ID`, `t0`.`OrdenDetalles_Color_ID`, `t0`.`OrdenDetalles_Orden_ID`

输出

Orden实体:

public partial class Orden
{
    public Orden()
    {
        Detalles = new HashSet<OrdenDetalles>();
    }

    public int Id { get; set; }
    public DateTime FechaRegistro { get; set; }
    public DateTime FechaRequerida { get; set; }
    public Estado Estatus { get; set; }

    public virtual ICollection<OrdenDetalles> Detalles { get; set; }
}

OrdenDetalles实体:

public partial class OrdenDetalles
{
    public OrdenDetalles()
    {
        ProductosAlmacenados = new HashSet<ProductoAlmacenado>();
    }

    public int ColorId { get; set; }
    public int OrdenId { get; set; }
    public int TelaId { get; set; }
    public int Cantidad { get; set; }

    public virtual Color Color { get; set; }
    public virtual Orden Orden { get; set; }
    public virtual Tela Tela { get; set; }
    public virtual ICollection<ProductoAlmacenado> ProductosAlmacenados { get; set; }
}

如果需要任何其他信息,请在评论中告诉我

使用 Select 进行投影通常是更好的方法,但是不要使用Select来填充 Entity 类,而是填充仅包含您需要的详细信息的 DTO 或 ViewModel POCO 类。

您不想仅使用所需的详细信息部分填充 Entity 类的原因是因为这些实例不再代表完整的实体,它们是实体的部分外壳。 任何期望接收实体的方法都可以通过这些不完整的实体之一传递,例如默认值和#nulls。 使用 DTO 定义可以避免混淆将通过哪些数据。

跨实体图查询时,您可以将相关实体的详细信息展平为字段或不同的结构。 虽然 SQL 乍一看并没有那么不同,但投影通常会减少通过网络传输的数据量,并且还允许您针对高使用情况调整索引,从而显着提高性能。

因此,例如,您可以将 DTO 展平为:

var ordenes = await _context.Ordenes
    .Select(o => new OrdenDto
    {
        Id = o.Id,
        Estatus = o.Estatus,
        FechaRegistro = o.FechaRegistro,
        FechaRequerida = o.FechaRequerida,
        Detalles = o.Detalles.Select(d => new OrdenDetallesDto
        {
            ColorNombre = d.Color.Nombre,
            TelaNombre = d.Tela.Nombre,
            Cantidad = d.Cantidad,
            ProductosAlmacenados = d.ProductosAlmacenados
        }).ToList()
    }).ToListAsync();

return ordenes;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM