如何使用 Include - ThenInclude 查询 IQueryable?
How to query IQueryable with Include - ThenInclude?
在 .NET Core 2.2 中,我坚持过滤 IQueryable
构建为:
_context.Ports.Include(p => p.VesselsPorts)
.ThenInclude(p => p.Arrival)
.Include(p => p.VesselsPorts)
.ThenInclude(p => p.Departure)
.OrderBy(p => p.PortLocode);
多对多关系。实体模型如:
public class PortModel
{
[Key]
public string PortLocode { get; set; }
public double? MaxKnownLOA { get; set; }
public double? MaxKnownBreadth { get; set; }
public double? MaxKnownDraught { get; set; }
public virtual ICollection<VesselPort> VesselsPorts { get; set; }
}
public class VesselPort
{
public int IMO { get; set; }
public string PortLocode { get; set; }
public DateTime? Departure { get; set; }
public DateTime? Arrival { get; set; }
public VesselModel VesselModel { get; set; }
public PortModel PortModel { get; set; }
}
基于此 this SO answer 我设法创建了这样的 LINQ:
_context.Ports.Include(p => p.VesselsPorts).ThenInclude(p => p.Arrival).OrderBy(p => p.PortLocode)
.Select(
p => new PortModel
{
PortLocode = p.PortLocode,
MaxKnownBreadth = p.MaxKnownBreadth,
MaxKnownDraught = p.MaxKnownDraught,
MaxKnownLOA = p.MaxKnownLOA,
VesselsPorts = p.VesselsPorts.Select(vp => vp.Arrival > DateTime.UtcNow.AddDays(-1)) as ICollection<VesselPort>
}).AsQueryable();
但我需要的是找到所有端口记录,其中:
VesselsPorts.Arrival > DateTime.UtcNow.AddDays(-1)
数量大于 int x = 5
值(例如)。我不知道该怎么做:/
感谢 @GertArnold 的评论,我最终得到了查询:
ports = ports.Where(p => p.VesselsPorts.Where(vp => vp.Arrival > DateTime.UtcNow.AddDays(-1)).Count() > x);
当使用 entity framework 时,人们倾向于使用 Include
而不是 Select
来节省他们的打字时间。这样做很少是明智的。
DbContext
持有一个ChangeTracker
。在 DbContext 的生命周期中从任何 table 中获取的每个完整行都存储在 ChangeTracker 中,以及一个克隆。您将获得对副本的引用。 (或者可能是对原件的引用)。如果您更改获得的数据的属性,它们会在 ChangeTracker 中的副本中更改。在 SaveChanges 期间,将原始文件与副本进行比较,以查看是否必须保存数据。
因此,如果您正在获取大量数据并使用 include
,那么每个获取的项目都会被克隆。这可能会大大降低您的查询速度。
除此克隆外,您获取的属性可能比您实际计划使用的要多。数据库管理系统在组合 tables 和在 tables 内搜索行方面得到了极大的优化。较慢的部分之一是将所选数据传输到您的本地进程。
例如,如果您有一个包含学校和学生的数据库,具有明显的一对多关系,那么每个学生都会有一个指向他就读的学校的外键。
因此,如果您要求 School [10] 和他的 2000 个 Students,那么每个 Student 的外键值都是 [10]。如果您使用 Include
,那么您将传输相同的值 10 超过 2000 次。多么浪费处理能力!
In entity framework, when querying data, always use Select
to select the properties, and Select only the properties that you actually plan to use. Only use Include
if you plan to change the fetched items.
当然不要使用 Include
以节省您的输入时间!
要求:给我港口和他们的船只
var portsWithTheirVessels = dbContext.Ports
.Where(port => ...) // if you don't want all Ports
.Select(port => new
{
// only select the properties that you want:
PortLocode = port.PortLoCode,
MaxKnownLOA = port.MaxKnownLOA,
MaxKnownBreadth = prot.MaxKnownBreadth,
MaxKnownDraught = ports.MaxKnownDraught,
// The Vessels in this port:
Vessels = port.VesselsPort.Select(vessel => new
{
// again: only the properties that you plan to use
IMO = vessel.IMO,
...
// do not select the foreign key, you already know the value!
// PortLocode = vessle.PortLocode,
})
.ToList(),
});
Entity framework 知道您的一对多关系,并且知道如果您使用虚拟 ICollection,它应该执行 (Group-)Join。
有些人更喜欢自己进行 Group-Join,或者他们使用不支持使用 ICollection 的 entity framework 版本。
var portsWithTheirVessels = dbContext.Ports.GroupJoin(dbContext.VesselPorts,
port => port.PortLocode, // from every Port take the primary key
vessel => vessel.PortLocode, // from every Vessel take the foreign key to Port
// parameter resultSelector: take every Port with its zero or more Vessels to make one new
(port, vesselsInThisPort) => new
{
PortLocode = port.PortLoCode,
...
Vessels = vesselsInThisPort.Select(vessel => new
{
...
})
.ToList(),
});
选择:
var portsWithTheirVessels = dbContext.Ports.Select(port => new
{
PortLocode = port.PortLoCode,
...
Vessels = dbContext.VesselPorts.Where(vessel => vessel.PortLocode == port.PortLocode)
.Select(vessel => new
{
...
}
.ToList(),
});
Entity framework 也会将其转换为 GroupJoin。
在 .NET Core 2.2 中,我坚持过滤 IQueryable
构建为:
_context.Ports.Include(p => p.VesselsPorts)
.ThenInclude(p => p.Arrival)
.Include(p => p.VesselsPorts)
.ThenInclude(p => p.Departure)
.OrderBy(p => p.PortLocode);
多对多关系。实体模型如:
public class PortModel
{
[Key]
public string PortLocode { get; set; }
public double? MaxKnownLOA { get; set; }
public double? MaxKnownBreadth { get; set; }
public double? MaxKnownDraught { get; set; }
public virtual ICollection<VesselPort> VesselsPorts { get; set; }
}
public class VesselPort
{
public int IMO { get; set; }
public string PortLocode { get; set; }
public DateTime? Departure { get; set; }
public DateTime? Arrival { get; set; }
public VesselModel VesselModel { get; set; }
public PortModel PortModel { get; set; }
}
基于此 this SO answer 我设法创建了这样的 LINQ:
_context.Ports.Include(p => p.VesselsPorts).ThenInclude(p => p.Arrival).OrderBy(p => p.PortLocode)
.Select(
p => new PortModel
{
PortLocode = p.PortLocode,
MaxKnownBreadth = p.MaxKnownBreadth,
MaxKnownDraught = p.MaxKnownDraught,
MaxKnownLOA = p.MaxKnownLOA,
VesselsPorts = p.VesselsPorts.Select(vp => vp.Arrival > DateTime.UtcNow.AddDays(-1)) as ICollection<VesselPort>
}).AsQueryable();
但我需要的是找到所有端口记录,其中:
VesselsPorts.Arrival > DateTime.UtcNow.AddDays(-1)
数量大于 int x = 5
值(例如)。我不知道该怎么做:/
感谢 @GertArnold 的评论,我最终得到了查询:
ports = ports.Where(p => p.VesselsPorts.Where(vp => vp.Arrival > DateTime.UtcNow.AddDays(-1)).Count() > x);
当使用 entity framework 时,人们倾向于使用 Include
而不是 Select
来节省他们的打字时间。这样做很少是明智的。
DbContext
持有一个ChangeTracker
。在 DbContext 的生命周期中从任何 table 中获取的每个完整行都存储在 ChangeTracker 中,以及一个克隆。您将获得对副本的引用。 (或者可能是对原件的引用)。如果您更改获得的数据的属性,它们会在 ChangeTracker 中的副本中更改。在 SaveChanges 期间,将原始文件与副本进行比较,以查看是否必须保存数据。
因此,如果您正在获取大量数据并使用 include
,那么每个获取的项目都会被克隆。这可能会大大降低您的查询速度。
除此克隆外,您获取的属性可能比您实际计划使用的要多。数据库管理系统在组合 tables 和在 tables 内搜索行方面得到了极大的优化。较慢的部分之一是将所选数据传输到您的本地进程。
例如,如果您有一个包含学校和学生的数据库,具有明显的一对多关系,那么每个学生都会有一个指向他就读的学校的外键。
因此,如果您要求 School [10] 和他的 2000 个 Students,那么每个 Student 的外键值都是 [10]。如果您使用 Include
,那么您将传输相同的值 10 超过 2000 次。多么浪费处理能力!
In entity framework, when querying data, always use
Select
to select the properties, and Select only the properties that you actually plan to use. Only useInclude
if you plan to change the fetched items.
当然不要使用 Include
以节省您的输入时间!
要求:给我港口和他们的船只
var portsWithTheirVessels = dbContext.Ports
.Where(port => ...) // if you don't want all Ports
.Select(port => new
{
// only select the properties that you want:
PortLocode = port.PortLoCode,
MaxKnownLOA = port.MaxKnownLOA,
MaxKnownBreadth = prot.MaxKnownBreadth,
MaxKnownDraught = ports.MaxKnownDraught,
// The Vessels in this port:
Vessels = port.VesselsPort.Select(vessel => new
{
// again: only the properties that you plan to use
IMO = vessel.IMO,
...
// do not select the foreign key, you already know the value!
// PortLocode = vessle.PortLocode,
})
.ToList(),
});
Entity framework 知道您的一对多关系,并且知道如果您使用虚拟 ICollection,它应该执行 (Group-)Join。
有些人更喜欢自己进行 Group-Join,或者他们使用不支持使用 ICollection 的 entity framework 版本。
var portsWithTheirVessels = dbContext.Ports.GroupJoin(dbContext.VesselPorts,
port => port.PortLocode, // from every Port take the primary key
vessel => vessel.PortLocode, // from every Vessel take the foreign key to Port
// parameter resultSelector: take every Port with its zero or more Vessels to make one new
(port, vesselsInThisPort) => new
{
PortLocode = port.PortLoCode,
...
Vessels = vesselsInThisPort.Select(vessel => new
{
...
})
.ToList(),
});
选择:
var portsWithTheirVessels = dbContext.Ports.Select(port => new
{
PortLocode = port.PortLoCode,
...
Vessels = dbContext.VesselPorts.Where(vessel => vessel.PortLocode == port.PortLocode)
.Select(vessel => new
{
...
}
.ToList(),
});
Entity framework 也会将其转换为 GroupJoin。