我有一个问题:
var ids = entities.Select(s => s.Id).OrderBy(s => s);
如果在检索其他实体时使用它进行向下筛选:
EntityService .For<OtherEntity>() .GetAll() .Where(s => ids.Contains(s.EntityId))
原始的ids查询会被执行多次吗?因此,最好在第二次调用之前把它带到内存中?
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的导航属性。关系可以是单向的或双向的。但最终我们可以使用其中任何一种,我建议使用单向导航属性,除非绝对需要双向导航。(如果需要更改实体之间的关系,则在重新关联这些关系时,它可以减少潜在的复杂性)在我们的实体中,我们会有这样的东西:
IN
JOIN
Join
[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中建立实体,并使用关系来查询数据。下一个潜在的问题是这样的语句:
d => d.Order.OrderDateTime.....
EntityService .For<OtherEntity>() .GetAll()
“获取全部()”看起来像是一个巨大的危险信号,如果这导致类似ToList()或AsEnumerable()对IQueryable由“For”方法返回。这样做将导致加载***所有***OtherEntity行到内存中之前,您应用您的Where子句。其后果应该是相当明显的。I'我对所有抽象EF DbContext的尝试都非常谨慎,尤其是泛型实现。这些往往不仅会给EF穿上紧身衣,导致性能和行为问题。
ToList()
AsEnumerable()
IQueryable
Where
1条答案
按热度按时间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的导航属性。关系可以是单向的或双向的。但最终我们可以使用其中任何一种,我建议使用单向导航属性,除非绝对需要双向导航。(如果需要更改实体之间的关系,则在重新关联这些关系时,它可以减少潜在的复杂性)
在我们的实体中,我们会有这样的东西:
因此,使用Order和Delivery之间的导航属性,如果我们想要在特定日期创建的所有订单的交货详细信息:
我们可以直接转到Delivery实体并查询
d => d.Order.OrderDateTime.....
在哪里,如果我们有一个对Order的双向引用的话。但是,这将只返回有交货的订单的交货。在第一个示例中,我返回一个匿名类型,它返回每个适用的Order ID。如果订单有交货,则返回交货。如果订单没有记录交货,则返回#null。我们需要考虑
IN
类型查询的情况可能是我们需要链接两个完全不同的数据库中的表,并且由不同的DbContext管理。当做类似这样的事情时,您应该考虑对数据进行分页,以确保生成的查询不会失控。在数据库之间设置链接视图是一个不错的选择,这样您就可以在单个DbContext中建立实体,并使用关系来查询数据。下一个潜在的问题是这样的语句:
“获取全部()”看起来像是一个巨大的危险信号,如果这导致类似
ToList()
或AsEnumerable()
对IQueryable
由“For”方法返回。这样做将导致加载***所有***OtherEntity行到内存中之前,您应用您的Where
子句。其后果应该是相当明显的。I'我对所有抽象EF DbContext的尝试都非常谨慎,尤其是泛型实现。这些往往不仅会给EF穿上紧身衣,导致性能和行为问题。