如何使用 LINQ group by 子句返回唯一的员工行?

Posted

技术标签:

【中文标题】如何使用 LINQ group by 子句返回唯一的员工行?【英文标题】:How do I use LINQ group by clause to return unique employee rows? 【发布时间】:2021-04-28 01:59:54 【问题描述】:

我对 LINQ 还很陌生,我一辈子都搞不明白。我看过很多关于如何在 LINQ 中使用 group by 的帖子,但由于某种原因,我无法让它工作。这在 ADO.NET 中非常简单,但我正在尝试使用 LINQ。这是我所拥有的与问题相关的内容。我已经标记了不起作用的部分。

public class JoinResult

    public int LocationID;
    public int EmployeeID;
    public string LastName;
    public string FirstName;
    public string Position;
    public bool Active;

private IQueryable<JoinResult> JoinResultIQueryable;
public IList<JoinResult> JoinResultIList;

JoinResultIQueryable = (
    from e in IDDSContext.Employee
    join p in IDDSContext.Position on e.PositionID equals p.PositionID
    join el in IDDSContext.EmployeeLocation on e.EmployeeID equals el.EmployeeID
    where e.PositionID != 1 // Do not display the super administrator's data.
    orderby e.LastName, e.FirstName

// ***** Edit: I forgot to add this line of code, which applies a filter
// ***** to the IQueryable. It is this filter (or others like it that I
// ***** have omitted) that causes the query to return multiple rows.
// ***** The EmployeeLocationsList contains multiple LocationIDs, hence
// ***** the duplicates employees that I need to get rid of. 
JoinResultIQueryable = JoinResultIQueryable
    .Where(e => EmployeeLocationsList.Contains(e.LocationID);
// *****


    // ***** The following line of code is what I want to do, but it doesn't work.
    // ***** I just want the above join to bring back unique employees with all the data.
    // ***** Select Distinct is way too cumbersome, so I'm using group by.
    group el by e.EmployeeID

    select new JoinResult
    
        LocationID = el.LocationID,
        EmployeeID = e.EmployeeID,
        LastName = e.LastName,
        FirstName = e.FirstName,
        Position = p.Position1,
        Active = e.Active
    )
    .AsNoTracking();

JoinResultIList = await JoinResultIQueryable
    .ToListAsync();

如何从 IQueryable 到 IList 只返回唯一的员工行?

***** 编辑: 这是我当前的输出:

[4][4][Anderson (OH)][Amanda][Dentist][True]
[5][4][Anderson (OH)][Amanda][Dentist][True]
[4][25][Stevens (OH)][Sally][Dental Assistant][True]
[4][30][Becon (OH)][Brenda][Administrative Assistant][False]
[5][30][Becon (OH)][Brenda][Administrative Assistant][False]

【问题讨论】:

在使用 linq 进行分组时,您应该使用 into 运算符,例如,将 e.EmployeeID 分组为 g,然后使用 g 是 select e.x ***.com/questions/7325278/group-by-in-linq 谢谢。我试了一下,但它对我不起作用。 :) 根据您的数据,您的位置和员工之间似乎存在一对多关系正确吗? 请看我下面的回答 是的。 Employee 和 EmployeeLocation 之间存在一对多的关系。 【参考方案1】:

其实这里不需要分组,而是Distinct。在Distinct 或分组之前订购是没有用的。也不需要带有自定义投影的AsNoTracking

var query =
   from e in IDDSContext.Employee
   join p in IDDSContext.Position on e.PositionID equals p.PositionID
   join el in IDDSContext.EmployeeLocation on e.EmployeeID equals el.EmployeeID
   where e.PositionID != 1 // Do not display the super administrator's data.
   select new JoinResult
   
        LocationID = el.LocationID,
        EmployeeID = e.EmployeeID,
        LastName = e.LastName,
        FirstName = e.FirstName,
        Position = p.Position1,
        Active = e.Active
   ;

query = query.Distinct().OrderBy(e => e.LastName).ThenBy(e => e.FirstName);

JoinResultIList = await query.ToListAsync();

【讨论】:

我希望这会奏效,但它没有。我意识到 Distinct 是我在 SQL 中想要的。感谢您对 AsNoTracking() 的提醒。 这对我不起作用:query = query.Distinct().OrderBy(e => e.LastName).ThenBy(e => e.FirstName);它没有改变任何东西。我也为你添加了输出,所以你可以看到。【参考方案2】:

问题是很少有员工拥有多个地点导致结果重复。您可以通过多种方式处理它。我使用Let 子句来解决以下示例中的问题

public class Employee


    public string FirstName  get; set; 
    public string LastName  get; set; 
    public int EmployeeID  get; set; 
    public int PositionID  get; set; 




public class EmployeeLocation

    public int EmployeeID  get; set; 
    public int LocationID  get; set; 


public class Position

    public int PositionID  get; set; 
    public string Position1  get; set; 


public class Location

    public int LocationID  get; set; 


public class JoinResult

    //Suggestion : Insetad of LocationID there should be a varibale that has all the locations of an employee
    public IEnumerable<int> LocationIDs;
    public int LocationID;
    public int EmployeeID;
    public string LastName;
    public string FirstName;
    public string Position;
    public bool Active;
    


 //Setting up mock data
        List<Position> positions = new List<Position>();
        positions.Add(new Position()  Position1 = "Dentist", PositionID = 2 );
        positions.Add(new Position()  Position1 = "Dental Assistant", PositionID = 3 );
        positions.Add(new Position()  Position1 = "Administrative Assistant", PositionID = 4 );

        List<Employee> employees = new List<Employee>();
        employees.Add(new Employee()  EmployeeID = 4, FirstName = "Amanda", LastName = "Anderson (OH)", PositionID = 2 );
        employees.Add(new Employee()  EmployeeID = 25, FirstName = "Sally", LastName = "Stevens (OH)", PositionID = 3 );
        employees.Add(new Employee()  EmployeeID = 30, FirstName = "Brenda", LastName = "Becon (OH)", PositionID = 4 );

        List<Location> locations = new List<Location>();
        locations.Add(new Location()  LocationID = 4 );
        locations.Add(new Location()  LocationID = 5 );

        List<EmployeeLocation> employeeLocation = new List<EmployeeLocation>();
        employeeLocation.Add(new EmployeeLocation()  LocationID = 4, EmployeeID = 4 );
        employeeLocation.Add(new EmployeeLocation()  LocationID = 5, EmployeeID = 4 );
        employeeLocation.Add(new EmployeeLocation()  LocationID = 4, EmployeeID = 25 );
        employeeLocation.Add(new EmployeeLocation()  LocationID = 4, EmployeeID = 30 );
        employeeLocation.Add(new EmployeeLocation()  LocationID = 5, EmployeeID = 30 );


        var result = (from e in employees
                 join p in positions on e.PositionID equals p.PositionID
                 let employeeLocations = (from el in employeeLocation where el.EmployeeID == e.EmployeeID select new  LocationID = el.LocationID )
                 where e.PositionID != 1 // Do not display the super administrator's data.
                 orderby e.LastName, e.FirstName
                 select new JoinResult
                 
                     LocationID = employeeLocations.Select(p=>p.LocationID).First()//Here its just selecting the first location,
                     LocationIDs = employeeLocations.Select(p=> p.LocationID),//This is my suggestion
                     EmployeeID = e.EmployeeID,
                     LastName = e.LastName,
                     FirstName = e.FirstName,
                     Position = p.Position1,
                 ).ToList();

【讨论】:

【参考方案3】:

好的。所以这是我想出的解决方案。我安装了 morelinq NuGet 包,其中包含 DistinctBy() 方法。然后我将该方法添加到问题中显示的代码的最后一行。

JoinResultIList = JoinResultIQueryable
    .DistinctBy(jr => jr.EmployeeID)
    .ToList();

【讨论】:

以上是关于如何使用 LINQ group by 子句返回唯一的员工行?的主要内容,如果未能解决你的问题,请参考以下文章

3-实体数据模型与LINQ-分组

LINQ to Sql 左外连接与 Group By 和 Have 子句

Postgresql“列必须出现在 GROUP BY 子句中或在聚合函数中使用”和唯一字段

LINQ Group By并将Group的子列表合并回唯一列表

如何使用 group by 和 order by LINQ DataTable 以删除重复数据

C# Linq group by 和 group by into 运用实例