【问题标题】:Construct queries in LINQ-to-Entities in Entity Framework ASP.NET MVC在 Entity Framework ASP.NET MVC 中的 LINQ-to-Entities 中构造查询
【发布时间】:2020-06-06 19:18:59
【问题描述】:

我很难创建满足我要求的 LINQ-to-Entities 查询。

我有两个表:BookingProcessStatusLog

预订表:

  • PN 编号 (PK)
  • 帐户名

ProcessStatusLog 表:

  • ID (PK)
  • PN 编号 (FK)
  • 保险代码
  • 状态
  • 已更新

以下是这些表的示例数据:

预订表

|----------|----------------|
| PNNumber | Account Name   |
|----------|----------------|
| 11111    | Boston Celtics |
|----------|----------------|
| 22222    | Miami Heat     |
|----------|----------------|

进程状态日志表

|------|-----------|---------------|--------------|-------------|
| ID   | PNNumber  | InsuranceCode | Status       | UpdatedOn   |
|------|-----------|---------------|--------------|-------------|
| 1    | 11111     | FIRE          | NEW          | 02/22/2020  |
|------|-----------|---------------|--------------|-------------|
| 2    | 11111     | FIRE          | FOR REVIEW   | 02/23/2020  |
|------|-----------|---------------|--------------|-------------|
| 3    | 22222     | FIRE          | NEW          | 02/24/2020  |
|------|-----------|---------------|--------------|-------------|
| 4    | 22222     | MORTGAGE      | NEW          | 02/25/2020  |
|------|-----------|---------------|--------------|-------------|
| 5    | 22222     | MORTGAGE      | FOR REVIEW   | 02/26/2020  |
|------|-----------|---------------|--------------|-------------|
| 6    | 22222     | FIRE          | CANCELLED    | 02/28/2020  |
|------|-----------|---------------|--------------|-------------|

现在,我想获取每个保险代码的单个 PN 的最新状态。

例如: 我想获取 PNNumber 11111 的最新状态。我想获取的输出是 “FOR REVIEW”。

对于 PNNumber 22222,所需的输出将是 "FOR REVIEW, CANCELLED"

如何为此编写 EF 查询? 谢谢。

【问题讨论】:

    标签: c# asp.net-mvc entity-framework linq linq-to-entities


    【解决方案1】:

    重新创建的数据库:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Booking>().HasKey(b => b.PNNumber);
        modelBuilder.Entity<Booking>().HasData(
            new Booking { PNNumber = "11111", AccountName = "Boston Celtics" },
            new Booking { PNNumber = "22222", AccountName = "Miami Heat" });
    
        modelBuilder.Entity<ProcessStatusLog>().HasData(
            new ProcessStatusLog { ID = 1, PNNumber = "11111", InsuranceCode = "FIRE", Status = "NEW", UpdatedOn = DateTime.Parse("22/02/2020") },
            new ProcessStatusLog { ID = 2, PNNumber = "11111", InsuranceCode = "FIRE", Status = "FOR REVIEW", UpdatedOn = DateTime.Parse("23/02/2020") },
            new ProcessStatusLog { ID = 3, PNNumber = "22222", InsuranceCode = "FIRE", Status = "NEW", UpdatedOn = DateTime.Parse("24/02/2020") },
            new ProcessStatusLog { ID = 4, PNNumber = "22222", InsuranceCode = "MORTGAGE", Status = "NEW", UpdatedOn = DateTime.Parse("25/02/2020") },
            new ProcessStatusLog { ID = 5, PNNumber = "22222", InsuranceCode = "MORTGAGE", Status = "FOR REVIEW", UpdatedOn = DateTime.Parse("26/02/2020") },
            new ProcessStatusLog { ID = 6, PNNumber = "22222", InsuranceCode = "FIRE", Status = "CANCELLED", UpdatedOn = DateTime.Parse("28/02/2020") });
    
        base.OnModelCreating(modelBuilder);
    }
    

    查询:

    public IActionResult Index()
    {
        this.dbContext.Database.EnsureCreated();
    
        string givenPNNumber = "22222";
    
        var filteredLogs = dbContext.ProcessStatusLog
            .Where(log => log.PNNumber == givenPNNumber);
    
        // Get date of the latest log for each incurance code for eaxh PNNumber.
        var groupedLogs = filteredLogs
            .GroupBy(psl => psl.InsuranceCode)
            .Select(group => new
            {
                Code = group.Key,
                LastUpdate = group.Max(g => g.UpdatedOn)
            });
    
        var result =
            from logs in filteredLogs
            join latestLogs in groupedLogs 
                on logs.InsuranceCode equals latestLogs.Code into joined
            from j in joined.DefaultIfEmpty()
            where logs.UpdatedOn == j.LastUpdate
            select logs.Status;
    
        string finalResult = string.Join(',', result.ToList());
        return View();
    }
    

    由于我只使用内存 Db,我不知道 EF 将如何生成查询,但查询计划是:

    • 仅获取给定PNNumber 的日志列表,
    • 从该过滤列表中获取给定PNNumberInsuranceCodes 列表以及该PNNumberInsuranceCode 的任何日志的最新日期
    • 加入结果(InsuranceCodeUpdatedOnMax(UpdatedOn) 的列表
    • 从该连接中仅选择 UpdatedOn == Max(UpdatedOn) 所在的行(给定 PNNumber 的所有 InsuranceCodes 的最新实体)
    • 选择Statuses
    • 加入他们。

    【讨论】:

    • 如果我想根据任何保险代码的最新状态获取整行预订怎么办?示例:*我过滤“CANCELLED”,我只会得到预订行 22222。*但是如果我过滤“FOR REVIEW”,我会得到预订行 11111 和 22222。假设发生了新的状态变化:{ ID = 7, PNNumber = "22222", InsuranceCode = "MORTGAGE", Status = "APPROVED", UpdatedOn = DateTime.Parse("29/02/2020") } 如果我现在过滤“FOR REVIEW”,我只会得到PNNumber 11111 的行,因为 22222 的最新状态现在是 CANCELED & APPROVED。
    • 这将涉及:通过InsuranceCode过滤日志表,通过PNNumber对第二个查询进行分组,在最后一个查询中加入3ed表并选择结果。
    • 非常感谢您提供的信息和直接的回答。大有帮助!
    【解决方案2】:

    我不知道它是否会直接转换为 SQL 或者您需要先获取整个列表,但是您应该可以通过使用 [GroupBy][1] 方法来实现。

    假设您的 Booking 模型有一个名为 ProcessStatusLogsProcessStatusLog 对象列表,它应该如下所示:

    var result = bookings.GroupBy(
      x => x.Id,
      (_, g) => g.SelectMany(x => x.ProcessStatusLogs)
                 .OrderByDescending(x => x.UpdatedOn)
                 .FirstOrDefault()
    );
    

    【讨论】:

      猜你喜欢
      • 2011-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多