linq 使用IQueryable,查询中包含:这个IQueryable会被执行多次吗

zlhcx6iw  于 2023-02-27  发布在  其他
关注(0)|答案(1)|浏览(172)

我有一个问题:

var ids = entities.Select(s => s.Id).OrderBy(s => s);

如果在检索其他实体时使用它进行向下筛选:

EntityService
      .For<OtherEntity>()
      .GetAll()
      .Where(s => ids.Contains(s.EntityId))

原始的ids查询会被执行多次吗?因此,最好在第二次调用之前把它带到内存中?

6ie5vjzr

6ie5vjzr1#

简短回答:“没有”。
长回答:你的例子看起来有更大的问题。
像EF这样的ORM擅长于将表之间的关系Map为对象结构。例如,我有一个Order表和一个Delivery表。每个Order有0或1个Delivery,这取决于它是否交付。因此,Delivery表有一个OrderId,或者Order表有一个DeliveryId,在最纯粹的例子中,Delivery表将使用OrderId作为它的PK和FK来巩固这种一对一/零的关系。
现在,在SQL中,有几种方法可以获得所需的交货。您可以通过查询Orders表来获得适用交货的OrderIds或DeliveryIds,然后使用IN子句针对这些Ids查询Deliveries表。或者,您可以在相应的FK上JOIN这两个表。
在您的示例中,您正在执行前一种方法,该方法可以正常工作,但有一些缺点。性能不佳,因为您将执行两次数据库往返,并且必须存储这些ID以提供给第二个查询。在第二个查询中,您可以提供给结果IN子句的值的数量也有限制。如果第一个查询可能返回100,000个订单ID,当然,当一个系统启动时,它只处理数百行或数千行,但用这种方法编写的代码很快就会陷入困境,并在系统背后得到一些真实世界的数据时突然失败。
EF支持后来我的导航属性的使用方式,或显式联接。如果您在相关表之间有FK,则导航属性是目前最好的方法。显式联接应保留用于需要处理无法使用FK的非规范化关系的极少数情况。例如,数据反模式(如使用OwnerType+的表OwnerId结构在多个其他表之间共享它们自己。典型的例子是地址表,其中不是使用联接表(如CustomerAddress和CompanyAddress)将地址链接到客户表而不是公司表,而是添加“客户”或“公司”的OwnerType,并将OwnerId设置为适用表中的相应Id。这里的缺点是,我们无法在Address和任何一个相关表之间建立FK关系,因为值与两个表都相关(导致对两者都不强制)在这些情况下,我们需要使用显式Join,并为OwnerType使用适用的过滤器,或者求助于IN方法。
通过导航属性,我们的Order实体将具有Delivery导航属性,该属性设置为使用两个表之间的OrderId或Order的DeliveryId,具体取决于关系的设置方式。如果需要,Delivery实体还可以具有返回Order的导航属性。关系可以是单向的或双向的。但最终我们可以使用其中任何一种,我建议使用单向导航属性,除非绝对需要双向导航。(如果需要更改实体之间的关系,则在重新关联这些关系时,它可以减少潜在的复杂性)
在我们的实体中,我们会有这样的东西:

[Table("Orders")]
public class Order
{
   [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int OrderId { get; set; }
    public DateTime OrderDateTime { get; set; }

   // Other order fields...

   public virtual Delivery Delivery { get; set; }
}

因此,使用Order和Delivery之间的导航属性,如果我们想要在特定日期创建的所有订单的交货详细信息:

startDate = startDate.Date; // strip any time.
var endDateTime = startDate.AddDays(1).AddTicks(-1);

var orderDeliveryDetails = _context.Orders
     .Where(o => o.OrderDateTime >= startDate && o.OrderDateTime <= endDateTime)
     .Select(o => new { o.OrderId, o.Delivery })
     .ToList();

我们可以直接转到Delivery实体并查询d => d.Order.OrderDateTime.....在哪里,如果我们有一个对Order的双向引用的话。但是,这将只返回有交货的订单的交货。在第一个示例中,我返回一个匿名类型,它返回每个适用的Order ID。如果订单有交货,则返回交货。如果订单没有记录交货,则返回#null。
我们需要考虑IN类型查询的情况可能是我们需要链接两个完全不同的数据库中的表,并且由不同的DbContext管理。当做类似这样的事情时,您应该考虑对数据进行分页,以确保生成的查询不会失控。在数据库之间设置链接视图是一个不错的选择,这样您就可以在单个DbContext中建立实体,并使用关系来查询数据。
下一个潜在的问题是这样的语句:

EntityService
  .For<OtherEntity>()
  .GetAll()

“获取全部()”看起来像是一个巨大的危险信号,如果这导致类似ToList()AsEnumerable()IQueryable由“For”方法返回。这样做将导致加载***所有***OtherEntity行到内存中之前,您应用您的Where子句。其后果应该是相当明显的。I'我对所有抽象EF DbContext的尝试都非常谨慎,尤其是泛型实现。这些往往不仅会给EF穿上紧身衣,导致性能和行为问题。

相关问题