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

C# 如何选择列的最大值并检索多个结果?,c#,sql-server,entity-framework,entity-framework-6,asp.net-web-api2,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 incl

我正在努力理解如何在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 
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 
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);
EntryTimestamp是一个Unix时间戳,它对于每个清单运行都是唯一的,并且对于在该运行中发现的所有软件都是相同的。现在,如果系统再次清点清单,该表将如下所示:

IdentityKey | ComputerID (FK) | Softwarename     | EntryTimestamp
------------+-----------------+------------------+--------------
          1 |               1 | Some Software    | 1547241345 
          2 |               1 | Another Software | 1547241345 
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 
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);
我想保留历史数据,所以我需要保留旧条目

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

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

IdentityKey | ComputerID (FK) | Softwarename       | EntryTimestamp
------------+-----------------+--------------------+---------------
          3 |               1 | Some Software      |     1886454564 
          4 |               1 | Another Software   |     1886454564 
          5 |               1 | Even More Software |     1886454564 
我考虑使用两个查询:

第一个查询:我检查EntryTimestamp的最大值 第二个问题:类似这样的问题:

IdentityKey | ComputerID (FK) | Softwarename     | EntryTimestamp
------------+-----------------+------------------+--------------
          1 |               1 | Some Software    | 1547241345 
          2 |               1 | Another Software | 1547241345 
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 
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

我还考虑只选择列EntryTimestamp的MAX;但是我甚至不能在查询代码中使用b.Computer\u Win\u Installed\u Software.EntryTimestamp

当我写:.Whereb=>b.Computer\u Win\u Installed\u Software时,它没有列出任何列作为可用选项

我认为这是因为EF中安装的计算机软件类属于ICollection类型。对于所有与Computers表具有1对多关系的其他表,这是相同的问题。 另一个表与Computers表有1对1的关系,在那里我可以选择所有列

我非常困惑

是的,我用谷歌搜索过,但我找不到任何帮助我的东西。到这里去的正确方法是什么

编辑:添加模型

DBContext:

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; }
}
计算机的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; }

    }
}
计算机软件的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 KysoWebApi
{
    using System;
    using System.Collections.Generic;

    public partial class Computer_Win_Installed_Software
    {
        public int ComputerId { get; set; }
        public string SoftwareName { get; set; }
        public Nullable<double> SoftwareVersion { get; set; }
        public Nullable<bool> IsServerSoftware { get; set; }
        public Nullable<int> SoftwareVendorId { get; set; }
        public string SoftwareIs32or64bits { get; set; }
        public int ComputerWinInstalledSoftwareEntryId { get; set; }
        public string EntryTimestamp { get; set; }

        public virtual Computer Computer { get; set; }
    }
}
请试试这个:

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

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列,所以请确保在其中输入正确的字段

它所做的是按最大日期加入记录

更新:我做了一个小的修正。它应该按软件名分组。根据您的喜好更改分组,使其正确地唯一地标识每个软件。

因此,对于给定的计算机,ICollection Computer\u Win\u Installed\u软件始终将包含所有相关行和所有入口时间戳。您不能轻易地删除包含的实体

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

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);
当然,我不确定这是你真正想要做的,你可能有一个

您是否可以尝试不包含相关对象,而只是查询它们

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

好的。经过大量的研究,我发现有几篇帖子说,你不能在include statements中过滤,这是一种要么全有要么全无的交易。最后的过滤总是会产生奇怪的结果

资料来源:

我找到了两种解决方案: 动态选择扩展:

似乎存在EntityFramework+EF+。我已经在Stackoverflow上看到了开发人员的帖子,尽管我现在找不到他们。他的库似乎能够做我想做的事情,但普通EF不行,而且似乎永远也不会,因为这项功能的要求可以追溯到几年前


谢谢大家努力帮助我并投入时间。现在,我将进行多个查询,可能稍后再进行检查,希望有更多的示例:D

您失败了,因为您没有匹配列的类型,EntryTimestamp的类型是什么?它与数据库中使用的类型相同吗?在数据库中是:varchar10,在我的代码中是字符串。但我甚至不能选择b.Computer\u Win\u Installed\u Software.EntryTimestamp。所有建模为ICollection的表似乎都是如此。这些与computers表有1对多的关系。您可以提供您的模型实体吗?顺便说一句,您可以在服务器端轻松地执行此操作:选择identitykey、computerid、softwarename、,entrytimestamp FROM computer\u win\u installed\u software其中entrytimestamp=SELECT MAXentrytimestamp FROM computer\u win\u installed\u software我在任何地方都看不到租户字段。这就是我尝试的。错误是:错误CS1061“ICollection”不包含“EntryTimestamp”的定义,并且找不到可访问的扩展方法“EntryTimestamp”接受类型为“ICollection”的第一个参数是否缺少using指令或程序集引用?只是确认一下,这与您在第行上发布的代码所包含的不同,您是否尝试过更改?您是否注意到您的实体中有不同的名称空间?
这是故意的吗?很抱歉,这不起作用。.Include中的所有内容都标记为错误:错误CS1660无法将lambda表达式转换为“string”类型,因为它不是委托类型。@soomon:我又看了一眼您的模型,它看起来像一个字符串,尽管它看起来像一个数字。再次尝试将Max语句更改为r.EntryTimestamp.ToString.Thank。是的,它是一根绳子。遗憾的是,错误仍然存在。我认为错误不在于您的代码,而在于这样一个事实,即IncludeAlright中似乎不允许使用lamda表达式,我需要使用System.Data.Entity命名空间来删除错误。关于EntryTimestamp是字符串的说法也是正确的。我把它改成了整数。代码仍然给我一个错误System.ArgumentException:Include路径表达式必须引用在类型上定义的导航属性。使用虚线路径作为参考导航属性,选择运算符作为集合导航属性。参数名称:System.Data.Entity.QueryableExtensions.Include[T,TProperty]IQueryable`1源,表达式`1路径我尝试过:.Includec=>c.Computer\u Win\u Installed\u Software.Whered=>d.EntryTimestamp==1540275242进行简单测试。它进行编译,但给出相同的错误。当我按下.d之后,它实际上会显示计算机的属性。所以它知道我想使用它们,但错误保持不变,这意味着如果表计算机\u Win\u安装的\u软件包含1000行,我将始终必须查询所有行并在以后进行筛选?或者不使用Include并在以后检索它们。您的数据库和您的实体似乎不一致——我不使用EF,但我想您应该将旧条目迁移到历史记录表中,以便您的实体希望所有当前行都连接起来。通过某些EF模型更改,可以创建仅用于当前行的EF属性,并自动修改最新行的联接。显然,EF方法通常是两个查询,使用Query和Load方法手动加载过滤对象。我添加了一种可能性,即不包括它们,只查询关系。