首页 > 解决方案 > LINQ GroupBy 生成不完整的查询

问题描述

我有一个非常简单的 LINQ 用例,现在我已经坚持了好几个小时。以下代码在列表中返回一个列表(1 个没有名称的组元素)。为什么它不能在 SQL 中正确翻译?我使用 EF Core 2.2。

我的 TileDetail 模型类:

using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;

namespace FAST_API.Models
{
    public class TileDetailsContext : DbContext
    {
        public TileDetailsContext(DbContextOptions<TileDetailsContext> options)
          : base(options)
        {
        }
        public DbQuery<TileDetail> TileDetailsItems{ get;set; }
    }
    [Table("WFD_MOTOR_EVENTS_AUFTRAEGE", Schema = "SF")]
    public class TileDetail
    {
        public string AUFTRAG { get; set; }
        public string PMPS { get; set; }
        public string ESN { get; set; }
        public string AUFTRAGSBEZ { get; set; }
        public int VORGANGSNR { get; set; }
        public string VORGANGSTEXT { get; set; }

        public char AUFTRAG_FERTIG { get; set;}
        public double? IST_BEGINN { get; set; }
        public double? IST_ENDE { get; set; }
        public DateTime? AG_IST_BEGINN {
            get { return (IST_BEGINN == null ? (DateTime?)null: new DateTime(1970,1,1).AddDays(Convert.ToDouble(IST_BEGINN))); }
        }
        public DateTime? AG_IST_ENDE {
            get { return (IST_ENDE == null ? (DateTime?)null: new DateTime(1970,1,1).AddDays(Convert.ToDouble(IST_ENDE))); }
        }

        public char VORGANG_FERTIG { get; set; }
        public bool AUFTRAG_FERTIG_STATUS {
            get { return AUFTRAG_FERTIG != 'J' ? true: false; }
        }
        public bool VORGANG_FERTIG_STATUS {
            get { return VORGANG_FERTIG != 'J' ? true: false; }
        }
    }
}

这是我使用 LINQ 的控制器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using FAST_API.Models;

namespace FAST_API.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class TileDetailsController : ControllerBase
    {
        private readonly TileDetailsContext _context;

        public TileDetailsController(TileDetailsContext context)
        {
            _context = context;
        }

        // GET: api/TileDetails
        [HttpGet]
        public IEnumerable<TileDetail> GetTileDetailsItems()
        {
            return _context.TileDetailsItems.ToList();
        }

        // GET: api/TileDetails/706722
        [HttpGet("{id}")]
        public async Task<IActionResult> GetTileDetails([FromRoute] string id)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            var tileDetails = _context.TileDetailsItems.Where(x => x.ESN == id);

            if (tileDetails == null)
            {
                return NotFound();
            }

            return Ok(tileDetails);
        }
        // GET: api/UniqueOrders/000096543182
        [HttpGet("UniqueOrders/{id}")]
        public async Task<IActionResult> GetUniqueOrders([FromRoute] string id)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            var tileDetails = _context.TileDetailsItems
            .Select(m => new {
                m.AUFTRAG, 
                m.AUFTRAGSBEZ, 
                m.PMPS,
                m.AUFTRAG_FERTIG_STATUS,
                m.ESN
            })
            .Where(x => x.ESN == id)
            .GroupBy(m => new {
                m.AUFTRAG, 
                m.AUFTRAGSBEZ, 
                m.PMPS,
                m.AUFTRAG_FERTIG_STATUS,
                m.ESN
            });

            if (tileDetails == null)
            {
                return NotFound();
            }

            return Ok(tileDetails);
        }
    }
}

我究竟做错了什么?我的期望是这样的查询:

SELECT "m"."AUFTRAG", "m"."AUFTRAGSBEZ", "m"."PMPS", "m"."AUFTRAG_FERTIG", "m"."ESN"
FROM "SF"."WFD_MOTOR_EVENTS_AUFTRAEGE" "m"
GROUP BY "m"."AUFTRAG", "m"."AUFTRAGSBEZ", "m"."PMPS", "m"."AUFTRAG_FERTIG", "m"."ESN"
HAVING "m"."ESN" = '567274'

相反,这是生成的(“GROUP BY”子句在哪里???):

SELECT "m"."AUFTRAG", "m"."AUFTRAGSBEZ", "m"."PMPS", "m"."AUFTRAG_FERTIG", "m"."ESN"
FROM "SF"."WFD_MOTOR_EVENTS_AUFTRAEGE" "m"
WHERE "m"."ESN" = :id_0
ORDER BY "m"."AUFTRAG" NULLS FIRST, "m"."AUFTRAGSBEZ" NULLS FIRST, "m"."PMPS" NULLS FIRST, "m"."AUFTRAG_FERTIG" NULLS FIRST, "m"."ESN" NULLS FIRST

标签: c#linqentity-framework-core

解决方案


在您的情况下,您的两个 sql 查询都是等效的。在“选择”部分,您具有与“组”部分相同的字段,并且没有任何聚合功能。中也没有聚合函数having。所以having在你的情况下像过滤器一样工作where

表达式GROUP BY "m"."AUFTRAG", "m"."AUFTRAGSBEZ", "m"."PMPS", "m"."AUFTRAG_FERTIG", "m"."ESN"意味着您按第一个字段分组,然后按第二个等,还按第一个字段排序结果,然后按第二个等等。因为您在“选择”部分没有任何聚合功能,并且选择部分的字段是相同的在“group by”部分,编译器将 group by 更改为 order by。

但在我看来,编译器应该Distinct在你的情况下添加到“选择”部分。

因此,您应该将代码简化为:

var tileDetails = _context.TileDetailsItems
            .Where(x => x.ESN == id)
            .Select(m => new {
                m.AUFTRAG, 
                m.AUFTRAGSBEZ, 
                m.PMPS,
                m.AUFTRAG_FERTIG_STATUS,
                m.ESN
            }).Distinct();

推荐阅读