如何在 Entity Framework Group By 结果中包含属性
Posted
技术标签:
【中文标题】如何在 Entity Framework Group By 结果中包含属性【英文标题】:How do I include attribute in Entity Framework Group By result 【发布时间】:2021-11-08 11:15:26 【问题描述】:假设我有一个位置表,其中包含位置 ID 和位置名称。假设我想获得每个位置的收入(在这个简单的场景中,我什至可能不需要GroupBy
- 但请假设我需要!)
var revenues = await _context.SaleTransaction.GroupBy(s => s.LocationId)
.Select(x => new LocationDTO
LocationId = x.Key,
LocationName = ???
Revenues = x.Sum(i => i.Amount)
).ToListAsync();
我试图作弊
LocationName = x.Select(i => i.Location.LocationName).First()
因为此 ID 的所有位置名称都相同。但 EF 无法翻译 First()
,除非我使用 AsEnumerable()
并将整个销售表放入应用程序内存中。
或者我可以第二次遍历结果:
foreach(var revenue in revenues)
revenue.LocationName = _context.Location.Find(revenue.LocationId).LocationName;
鉴于位置的数量是固定的(并且相对较少),它可能是最好的方法。尽管如此,对于每个位置O(n)
或将整个位置列表拉入内存都不是很好。也许有一种方法可以将 LocationName(和其他一些属性)分配为 GroupBy 语句的一部分。
我正在使用 EF Core 5;或者,如果 EF Core 6 中出现了某些东西 - 那也可以。
【问题讨论】:
我认为从位置表而不是 SaleTransaction 开始查询会更好 @SinaRiani 好的。想解释一下原因吗? 请告诉我,Location 和 SaleTransaction 之间的关系类型是什么? “一对一”还是“一对多”? Location 和 SaleTransaction 的关系类型?严重地?当然是一对多 可以解决您的问题的方法是按 id 和名称分组参见 ***.com/questions/2421388/… 【参考方案1】:我可以简要地看到,您需要一个 linq 连接查询才能加入搜索。使用 EF linq 查询意味着它们在使用之前不会被加载到内存中,因此它可以解决加载整个表的问题。
你可以这样写:
var revenues = await _context.SaleTransactions.Join(_context.Locations, s => s.LocationId, l => l.Id, (s, l) => new LocationId = s.LocationId, LocationName = l.LocationName, Revenues = s.Sum(i => i.Amount));
我会将整个小提琴与您可能的模型的模拟联系起来 https://dotnetfiddle.net/BGJmjj
【讨论】:
不,加入绝对不是要走的路。可以(并且应该)使用导航属性。 我喜欢!我对加入 EF 持怀疑态度 - 它经常反映糟糕的设计(我 具有 从 SaleTransaction 到 Location 的导航属性);但在这种情况下,这可能是正确的方法。让我玩一下吧 @GertArnold 在 EF Core 中很容易包含导航属性(尤其是在 .net 5 中),您可以再包含一个 Location 类型的属性,无论如何您都必须保留它的 Id,或者在一个与可能的关系,所以我认为这不是什么大问题 什么不是问题?在导航属性可用时使用 join 对我来说是个问题。【参考方案2】:您可以按多个值进行分组。例如;
var revenues = await _context.SaleTransaction
.GroupBy(s => new
s.LocationId,
s.Location.Name
)
.Select(x => new LocationDTO
LocationId = x.Key.LocationId,
LocationName = x.Key.Name,
Revenues = x.Sum(i => i.Amount)
).ToListAsync();
虽然您似乎正在计算每个位置的总数,但在这种情况下,您可以改为围绕位置构建查询。
var revenues = await _context.Location
.Select(x => new LocationDTO
LocationId = x.Id,
LocationName = x.Name,
Revenues = x.SaleTransactions.Sum(i => i.Amount)
).ToListAsync();
【讨论】:
我的 LINQ 语句要复杂得多;正如我在 OP 中所说的那样——我意识到通过我所做的简化,我什至不需要GroupBy
——但相信我……我愿意;)第一个陈述——想象有十几个位置属性;有些可能非常复杂(例如,地址、经理等)。所以,虽然我意识到我可以将所有“卷心菜”放入 GroupBy 语句中 - 然后我宁愿遍历结果列表
您可以选择(或分组)整个实体,例如.Select(x => new x.Location, ... )
。尽管您可能需要显式加载拥有的类型。或许你应该确保你的例子更真实。【参考方案3】:
var revenues = await _context.Location
.Select(x => new LocationDTO
LocationId = x.Id,
LocationName = x.Name,
Revenues = x.SaleTransactions.Sum(i => i.Amount)
).ToListAsync();
有一个例子: .NetFiddle
【讨论】:
谢谢。正如我所说,在这个简单的场景中,我什至可能不需要 GroupBy - 但请假设我需要! 试试LocationName = x.First().Location.LocationId
以上是关于如何在 Entity Framework Group By 结果中包含属性的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Entity Framework Core 中运行存储过程?
如何在 Entity Framework Core 中运行存储过程?
如何在 Entity Framework Code First 中使属性唯一