简体   繁体   English

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

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

I'm trying to get the most efficient query for selecting some nested attributes in navigation properties so the API sends a response which contains only relevant information.我试图获得最有效的查询来选择导航属性中的一些嵌套属性,以便 API 发送仅包含相关信息的响应。 So far I tried the Include() and ThenInclude() approach and the answer on this post .到目前为止,我尝试了Include()ThenInclude()方法以及这篇文章的答案。

However both approaches produced similar queries and retrieved the same columns from the database.然而,这两种方法都产生了类似的查询并从数据库中检索了相同的列。 The only noticeable difference being that the Include approach loaded the data from unneeded columns into the entity object attributes while the Select() approach didn't.唯一明显的区别是 Include 方法将不需要的列中的数据加载到实体对象属性中,而Select()方法没有。 I'm using a layer pattern so using null type isn't much of an option for me and the nul values in the entity aren't a problem as only the retrieves values are eventually loaded into the dto, but still the query efficiency question remains.我正在使用图层模式,因此使用 null 类型对我来说不是一个选项,并且实体中的 nul 值不是问题,因为只有检索值最终加载到 dto 中,但仍然是查询效率问题遗迹。

My question is if there is a better way to query single columns from navigation properties and if not which of the two mentioned ways is better.我的问题是,是否有更好的方法从导航属性中查询单个列,如果没有,上述两种方法中的哪一种更好。

Include query method:包括查询方法:

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() query method: 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;
}

Query generated by Select with output: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 output MySQlWorkbench 输出

Query generated by Include() : 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`

Output输出

Orden entity: 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 entity: 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; }
}

If any other information is needed please let me know in the comments如果需要任何其他信息,请在评论中告诉我

Projection using Select is generally the better approach however, do not use Select to populate Entity classes, rather populate DTOs or ViewModel POCO classes that contain just the details you need.使用 Select 进行投影通常是更好的方法,但是不要使用Select来填充 Entity 类,而是填充仅包含您需要的详细信息的 DTO 或 ViewModel POCO 类。

The reason you don't want to populate Entity classes partially with just the details you need is because those instances no longer represent complete entities, they are partial shells of entities.您不想仅使用所需的详细信息部分填充 Entity 类的原因是因为这些实例不再代表完整的实体,它们是实体的部分外壳。 Any method that expects to receive an entity could be passed one of these incomplete entities with things like default values and #nulls.任何期望接收实体的方法都可以通过这些不完整的实体之一传递,例如默认值和#nulls。 Using a DTO definition avoids any confusion about what data would be coming through.使用 DTO 定义可以避免混淆将通过哪些数据。

When querying across an entity graph you can flatten the details from related entities into fields or a different structure.跨实体图查询时,您可以将相关实体的详细信息展平为字段或不同的结构。 While the SQL won't look that different at a glance, projection generally reduces the amount of data traveling over the wire and also allows you to tune indexing for high-usage scenarios which can significantly improve performance.虽然 SQL 乍一看并没有那么不同,但投影通常会减少通过网络传输的数据量,并且还允许您针对高使用情况调整索引,从而显着提高性能。

So for instance you could flatten the DTO to something like:因此,例如,您可以将 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