如何使用Include-TheInInclude查询IQueryable



在.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; }
}

基于这个SO答案,我设法创建了这样的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);

当使用实体框架时,人们倾向于使用Include而不是Select来节省一些类型。这样做很少是明智的

CCD_ 6保存一个CCD_。在DbContext的生存期内从任何表中提取的每一整行都存储在ChangeTracker中,以及一个克隆中。你会得到一份副本的参考资料。(或者可能是对原作的引用)。如果更改所获得数据的属性,则ChangeTracker中的副本中的属性也会发生更改。在SaveChanges过程中,将原始数据与副本进行比较,以查看是否必须保存数据。

因此,如果您正在获取相当多的数据,并使用include,那么每个获取的项都会被克隆。这可能会大大降低查询速度。

除了此克隆之外,您可能会获取比实际计划使用的更多的属性。数据库管理系统在组合表和搜索表中的行方面进行了极大的优化。较慢的部分之一是将选定的数据传输到本地进程。

例如,如果你有一个学校和学生的数据库,具有明显的一对多关系,那么每个学生都将拥有他就读学校的外键。

因此,如果你要求学校[10]有他的2000名学生,那么每个学生都会有一个外键值[10]。如果您使用Include,那么您将在2000多次传输10次相同的值。真是浪费处理能力!

在实体框架中,查询数据时,始终使用Select来选择属性,并仅选择您实际计划使用的属性。如果您计划更改提取的项目,请仅使用Include

当然不要使用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(),
});

实体框架知道您的一对多关系,并且知道如果您使用虚拟ICollection,它应该执行(Group-)Join。

有些人更喜欢自己进行Group Join,或者他们使用的实体框架版本不支持使用ICollection。

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(),

});

实体框架也会将其转换为GroupJoin。

最新更新