微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用 Include - ThenInclude 查询 IQueryable?

如何解决如何使用 Include - ThenInclude 查询 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; }
}

基于此 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);
,

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

DbContext 包含 ChangeTracker。您在 DbContext 的生命周期内获取的任何表中的每个完整行都存储在 ChangeTracker 中,以及一个克隆。您将获得对副本的引用。 (或者可能是对原文的引用)。如果您更改获得的数据的属性,则它们会在 ChangeTracker 中的副本中更改。在 SaveChanges 期间,将原始数据与副本进行比较,以查看是否必须保存数据。

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

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

例如,如果您有一个包含 Schools 和 Students 的数据库,并且具有明显的一对多关系,那么每个 Student 都会有一个指向他就读学校的外键。

因此,如果您要求 School [10] 和他的 2000 个学生,那么每个学生的外键值为 [10]。如果您使用 Include,那么您将转移这个相同的值 10 次超过 2000 次。多么浪费处理能力!

在实体框架中,查询数据时,总是使用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。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。