C#中的Lambda表达式分组

9
我想通过ItemNumber对LINQ查询结果进行分组,并返回包含Quantity总数的完整表格。
Example:
ItemNumber - ItemName - Quantity
100          Item1       1
150          Item2       2
100          Item1       2
200          Item3       1
150          Item2       2

Should be:
ItemNumber - ItemName - Quantity
100          Item1       3
150          Item2       4
200          Item3       1

这是我正在尝试分组的查询:

public IQueryable<WebsiteOrderStatus> GetOrderStatusByAccountNumberWithoutDeleted
        (string accountNumber)
{
    return db.WebsiteOrderStatus
             .Where(x => x.AccountNumber == accountNumber && x.LastUpdatedStatus != 1);
}

到目前为止,我取得的最佳结果是(尽管这不能编译):

public IQueryable<IGrouping<Int32?, WebsiteOrderStatus>> lol(string accountNumber)
{
     db.WebsiteOrderStatus
       .Where(x => x.AccountNumber == accountNumber && x.LastUpdatedStatus != 1)
       .GroupBy(g => g.ItemNumber)
       .Select(g => new
                    {
                         g.Key.ItemNumber,
                         Column1 = (Int32?)g.Sum(p => p.Quantity)
                    });
 }

编辑:

谢谢大家的回复,我必须面对这个问题。在我看来,这些匿名类型很难处理,所以我找到了另一个解决方案。

我创建了另一种方法,它对用户物品的数量进行求和,并将第一个进行分组。

public IQueryable<WebsiteOrderStatus> GetOrderStatusByAccountNumberWithoutDeleted(string accountNumber)
{
    return db.WebsiteOrderStatus.Where(x => x.AccountNumber == accountNumber && x.LastUpdatedStatus != 1).GroupBy(x => x.ItemNumber).Select(grp => grp.First());
}

public int GetQuantityOfUsersItem(string accountNumber, string itemNumber)
{
    return db.WebsiteOrderStatus.Where(x => x.ItemNumber == itemNumber && x.AccountNumber == accountNumber).Sum(x => x.Quantity);
}

在我拥有GridView的页面上,我做了以下操作:
var query = websiteOrderStatusRep.GetOrderStatusByAccountNumberWithoutDeleted(AppSession.CurrentLoginTicket.AccountNumber).Select(x => new { x.ItemName, x.ItemNumber, x.FormatName, x.Price, x.Status, x.Levering, Quantity = websiteOrderStatusRep.GetQuantityOfUsersItem(x.AccountNumber, x.ItemNumber)});

1
不确定还有什么问题,但至少第二个示例中缺少一个 return - Ani
你遇到了什么编译错误? - Daniel Hilgarth
1
可能他试图把g.Key当作一个对象处理,但实际上它只是一个int。如果他想在结果中得到项目名称,那么他还必须将其存储在键对象中(并使键成为可比较的对象),或从组枚举器的第一个条目中读取它。 - Rup
5个回答

11
public IQueryable<IGrouping<Int32?, WebsiteOrderStatus>> lol(string accountNumber)
{
     db.WebsiteOrderStatus
       .Where(x => x.AccountNumber == accountNumber && x.LastUpdatedStatus != 1)
       .GroupBy(g => g.ItemNumber)
       .Select(g => new
                    {
                         ItemNumber = g.Key,
                         ItemName = g.First().ItemName,
                         Count = g.Sum(item => item.Quantity)
                    });
 }

7
 public IQueryable<OrderStatus > lol(string accountNumber)
 {
     return db.WebsiteOrderStatus
        .Where(x => x.AccountNumber == accountNumber && x.LastUpdatedStatus != 1)
        .GroupBy(g => g.ItemNumber)
        .Select(g => 
                new OrderStatus //This is your custom class, for binding only
                {
                     ItemNumber = g.Key,
                     ItemName = g.First().ItemName,
                     Quantity = g.Sum(g => g.Quantity)
                }
        );
 }

我们快要完成了 - 直到我尝试将查询绑定到网格视图之前都没有错误。 我收到了这个异常:System.NotSupportedException: 不允许在查询中显式构造实体类型 'GWportal.BusinessLayer.Repository.WebsiteOrderStatus'。 - KLIM8D
2
实体可以在查询之外创建,并使用DataContext插入到数据存储中。然后,您可以使用查询检索它们。但是,您无法在查询的一部分中创建实体。在这种情况下,您需要创建一个自定义类来绑定并将“WebsiteOrderStatus”映射到该类的对象。 - Amar Palsapure

6
我认为应该将 Select 改为:
   .Select(g => new
                {
                     ItemNumber = g.Key,
                     Column1 = (Int32?)g.Sum(p => p.Quantity)
                });

注意匿名类型第一行的更改。分组的键已经是项目编号。


5
我看到你的查询中存在以下问题:
  1. 缺少 return 语句,根据注释应该添加
  2. 选择语句应该是:

-

.Select(g => new {
       ItemNumber = g.Key,
       Total = g.Sum(p => p.Quantity)
    });

编辑:如果您想在结果对象中获取物品编号(ItemNumber)和物品名称(ItemName),您还必须按这些字段进行分组。

db.WebsiteOrderStatus
   .Where(x => x.AccountNumber == accountNumber && x.LastUpdatedStatus != 1)
   .GroupBy(g => new { g.ItemNumber, g.ItemName })
   .Select(g => new
                {
                     ItemNumber = g.Key.ItemNumber,
                     ItemName = g.Key.ItemName,
                     Count = g.Sum(item => item.Quantity)
                });

似乎它只返回了ItemNumber和Total。我想要的是整个表格,只有一个名为Total的列,其中包含数量的总计。 - KLIM8D
好的,所以我想要在结果中获取的每个对象都必须在分组中。实际上,我在表中还有其他5个字段,除了需要包含在groupby中的所有字段之外,是否有更好的方法来进行这种分组? - KLIM8D
@KLIM8D - 不,根据定义,你想在 Group 输出中包含的每个字段(即未聚合的字段)都必须在 GroupBy 中。 (注意:你可以使用另一个建议,即使用 .First(),但我不建议这样做,因为每次枚举列表) - Jamiec

2

你不能使用匿名类型作为返回值类型。因此,你永远无法编译代码。

此外,你的linq表达式具有IQueryable< [匿名类型] >结果类型。

我相信你可以像这样做:

public IQueryable<OrderStatus> lol(string accountNumber) 
{ 
     db.WebsiteOrderStatus 
       .Where(order => order.AccountNumber == accountNumber && order.LastUpdatedStatus != 1) 
       .GroupBy(order => order.ItemNumber) 
       .Select(grouping => new OrderStatus //This is your custom class, for binding only
                    { 
                         ItemNumber = grouping.Key, 
                         ItemName = grouping.First().ItemName, 
                         Quantity = grouping.Sum(order => order.Quantity) 
                    }); 
}

我已经修正了我的回答 :)

为什么不能使用匿名类型作为返回值? - Jamiec
1
你如何在方法的签名中指定它? - Vitali Vishneuski
您不能将字段、属性、事件或方法的返回类型声明为匿名类型。同样,您也不能将方法、属性、构造函数或索引器的形式参数声明为匿名类型。要将匿名类型或包含匿名类型的集合作为参数传递给方法,可以将参数声明为 object 类型。但是,这样做会破坏强类型的目的。如果必须存储查询结果或将其传递到方法边界之外,请考虑使用普通命名的结构体或类,而不是匿名类型。 - Vitali Vishneuski
谢谢,我以前没有使用过自定义类。如果我发现我的解决方案(编辑主题)性能慢并且有时间,我会尝试一下的 :) - KLIM8D

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接