如何选择一列的最大值并检索多个结果?

时间:2018-10-22 19:18:07

标签: c# sql-server entity-framework entity-framework-6 asp.net-web-api2

我正在努力了解如何在ASP.NET Web API v2项目中使用Entity Framework 6和SQL Server构造SQL查询。

我的表结构:Computers表是我的软件已清单的所有计算机的根表。它包含列ComputerID,该列被其他表用作外键。

通过使用以下查询,我可以检索包括我拥有的所有信息的计算机:

Computer computer = ComputersDbContext.Computers
                   ... more includes....
                    .Include("Computer_Win_Installed_Software")
                   ... more includes....
                    .Where(a => a.ComputerId == computerId)
                       .First(t => t.TenantId == tenantId);

如您所见,我还有另一个名为Computer_Win_Installed_Software的表,该表存储系统上安装的所有软件。看起来像这样:

IdentityKey | ComputerID (FK) | Softwarename     | EntryTimestamp
------------+-----------------+------------------+--------------
          1 |               1 | Some Software    | 1547241345 
          2 |               1 | Another Software | 1547241345 

EntryTimestamp是Unix时间戳记,它对于每个清单运行都是唯一的,并且对该运行中发现的所有软件都相同。现在,如果系统再次进行盘点,则表将如下所示:

IdentityKey | ComputerID (FK) | Softwarename       | EntryTimestamp
------------+-----------------+--------------------+---------------
          1 |               1 | Some Software      |     1547241345 
          2 |               1 | Another Software   |     1547241345 
          3 |               1 | Some Software      |     1886454564 
          4 |               1 | Another Software   |     1886454564 
          5 |               1 | Even More Software |     1886454564 

我想保留历史数据,所以我需要保留旧条目。

我的问题是,我从上面进行的EF查询将在结果对象中返回所有这些条目。

如何更改查询以使其仅返回:

IdentityKey | ComputerID (FK) | Softwarename       | EntryTimestamp
------------+-----------------+--------------------+---------------
          3 |               1 | Some Software      |     1886454564 
          4 |               1 | Another Software   |     1886454564 
          5 |               1 | Even More Software |     1886454564 

我考虑使用2个查询:

  • 第一个查询:我检查EntryTimestamp的最大值
  • 第二个查询:类似这样的内容:

    Computer computer = ComputersDbContext.Computers
                            .Include("Computer_Win_Installed_Software")      
                            .Where(a => a.ComputerId == computerId)
                            .Where(b => b.Computer_Win_Installed_Software.EntryTimestamp == EntryTimestamp)
                            .First(t => t.TenantId == tenantId);
    

但是Intellisense立即对我尖叫:D

我还考虑过只选择列MAX()的{​​{1}};但我什至不能在查询代码中使用EntryTimestamp

当我写:b.Computer_Win_Installed_Software.EntryTimestamp时,它没有列出任何列作为可用选项。

我认为这是因为EF中的.Where(b => b.Computer_Win_Installed_Software类的类型为Computer_Win_Installed_Software。对于其他所有与ICollection<Computer_Win_Installed_Software>表具有一对多关系的表,这也是同样的问题。 另一个表与Computers表具有1到1的关系,在这里我可以选择所有列。

我很困惑。

是的,我做了google,但是找不到任何可以帮助我的东西。去这里的正确方法是什么?

编辑:添加的模型

DBContext:

Computers

计算机的EF模型:

public class DbEntities : DbContext
{
    public virtual DbSet<Agent> Agents { get; set; }     
    public virtual DbSet<Computer_Win_Installed_Software> ComputerWinInstalledSoftware { get; set; }      
    public DbSet<Computer> Computers { get; set; }
}

Computer_Win_Installed_Software的EF模型:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated from a template.
//
//     Manual changes to this file may cause unexpected behavior in your application.
//     Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace ProjectName
{
    using System;
    using System.Collections.Generic;

    public partial class Computer
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        public Computer()
        {
            this.Computer_Win_Installed_Software = new HashSet<Computer_Win_Installed_Software>();            
        }

        public int ComputerId { get; set; }
        public int TenantId { get; set; }
        public virtual Agent Agent { get; set; }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public virtual ICollection<Computer_Win_Installed_Software> Computer_Win_Installed_Software { get; set; }

    }
}

4 个答案:

答案 0 :(得分:1)

因此,对于给定的ComputerICollection<Computer_Win_Installed_Software> Computer_Win_Installed_Software将始终包括所有EntryTimestampts的所有相关行。您无法(轻松)filter包含的实体。

因此,只需返回过滤的实体:

var computer = ComputersDbContext.Computers
                .Where(a => a.ComputerId == computerId)
                .First(t => t.TenantId == tenantId);
var cwis = computer
            .Computer_Win_Installed_Software
                .Where(b => b.EntryTimestamp == EntryTimestamp);

当然,我不确定这是您实际想要做的,您可能有一个XY Problem

您可以尝试不包括相关对象,而只是查询它们吗?

var computer = ComputersDbContext.Computers
                .Where(a => a.ComputerId == computerId)
                .First(t => t.TenantId == tenantId);
                .Where(b => b.Computer_Win_Installed_Software.EntryTimestamp == EntryTimestamp);

答案 1 :(得分:0)

请尝试:

            var computer = db.ComputerWinInstalledSoftware
                .Include("Computer")
                .First(c => c.Computer.ComputerId == computerId 
                && c.Computer.TenantId == tenantId 
                && c.EntryTimestamp == EntryTimestamp ).Computer;

答案 2 :(得分:0)

尝试一下:

Computer computer = context.Computers
   .Include(c => c.Computer_Win_Installed_Software.Join(
       c.Computer_Win_Installed_Software.Where(a => a.ComputerId == computerId && a.Computer.TenantId == tenantId)
       .GroupBy(s => s.Softwarename)
       .Select(g => new { SoftwareName = g.Key, MaxDate = g.Max(r => r.EntryTimestamp) })
       ,o => o.EntryTimestamp
       ,i => i.MaxDate
       , (o,i) => o))
   .First(a => a.ComputerId == computerId && a.TenantId == tenantId);

在您的实体定义的任何位置都看不到TenantId列,因此请确保在其中放置正确的字段。

这是在最大日期之前加入记录。

更新:我做了一个小更正。它应该按软件名称分组。根据您的喜好更改分组,以便正确识别每个软件。

答案 3 :(得分:0)

好的。经过大量研究,我发现有几篇文章指出您不能在包含陈述中进行过滤,这是一种全有或全无的交易。最后的过滤始终会产生奇怪的结果。

来源: Entity Framework - Selective Condition on Included Navigation Property

Does Include load all related entities or the specified ones?

我能够找到2个解决方案: DynamicSelectExtensions: https://github.com/thiscode/DynamicSelectExtensions

似乎有EntityFramework +(EF +)。我目前在Stackoverflow上看到过开发人员的帖子,尽管目前找不到。他的库似乎可以执行我想做的事情,但是正常的EF不能,而且似乎永远也不会,因为对此功能的要求可以追溯到几年前。

感谢大家尝试帮助我并投入您的时间。现在,我将进行多个查询,也许以后再回来,希望有更多的经验:D