英文:
How can I make EF Core 3 translate the group by foreign key the same way efcore7 does it?
问题
我有以下的实体和数据库上下文:
public class Item
{
public int Id { get; set; }
public int? ReceiptId { get; set; }
public int ItemTypeId { get; set; }
}
public class ItemType
{
public int Id { get; set; }
public string Name { get; set; }
public List<Item> Items { get; set; }
}
public class Receipt
{
public int Id { get; set; }
public string ReceiptInfo { get; set; }
public List<Item> Items { get; set; }
}
我试图获取收据的列表,但不想包含它们包含的项目,而是想要它们包含“itemType”和每种项目的数量。我编写了以下的 LINQ 查询,它有效:
var result = _databaseContext.Receipts.Select(r => new
{
r.Id,
r.ReceiptInfo,
ItemInfo = r.Items.GroupBy(item => item.ItemTypeId)
.Select(group => new
{
IdItemType = group.Key,
AmountOfItems = group.Count(),
}).ToList()
});
在 EF Core 7 中,它被转换为以下的 SQL 查询:
SELECT [r].[Id], [r].[ReceiptInfo], [t].[IdItemType], [t].[AmountOfItems]
FROM [Receipts] AS [r]
OUTER APPLY
(SELECT [i].[ItemTypeId] AS [IdItemType], COUNT(*) AS [AmountOfItems]
FROM [Items] AS [i]
WHERE [r].[Id] = [i].[ReceiptId]
GROUP BY [i].[ItemTypeId]) AS [t]
ORDER BY [r].[Id]
然而,我需要在一个不支持 EF Core 3.1 以前版本的旧项目中执行此操作。在那里,查询被转换为不同的方式,并且我会收到以下错误:
选择列表中的列 'Receipts.Id' 无效,因为它既不包含在聚合函数中,也不包含在 GROUP BY 子句中。
英文:
I have the following entities and a database context
public class Item
{
public int Id { get; set; }
public int? ReceiptId { get; set; }
public int ItemTypeId { get; set; }
}
public class ItemType
{
public int Id { get; set; }
public string Name { get; set; }
public List<Item> Items { get; set; }
}
public class Receipt
{
public int Id { get; set; }
public string ReceiptInfo { get; set; }
public List<Item> Items { get; set; }
}
I'm trying to get a the list of receipts, but instead of containing the items they contain, I want them to have the itemType
and the amount of items for each. I have written the following linq query, which works:
var result = _databaseContext.Receipts.Select(r => new
{
r.Id,
r.ReceiptInfo,
ItemInfo = r.Items.GroupBy(item => item.ItemTypeId)
.Select(group => new
{
IdItemType = group.Key,
AmountOfItems = group.Count(),
}).ToList()
});
With EF Core 7, it is translated to the following SQL query:
SELECT [r].[Id], [r].[ReceiptInfo], [t].[IdItemType], [t].[AmountOfItems]
FROM [Receipts] AS [r]
OUTER APPLY
(SELECT [i].[ItemTypeId] AS [IdItemType], COUNT(*) AS [AmountOfItems]
FROM [Items] AS [i]
WHERE [r].[Id] = [i].[ReceiptId]
GROUP BY [i].[ItemTypeId]) AS [t]
ORDER BY [r].[Id]
Yet, I need to do this in an older project which doesn't support a version older than 3.1 for EF Core.
There it translates the query differently and I get this error
> Column 'Receipts.Id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause
答案1
得分: 1
在 EF Core 3.1 的情况下,您需要在客户端端对加载的详细项进行后处理:
var rawData = _databaseContext.Receipts.Select(r => new
{
r.Id,
r.ReceiptInfo,
RawItemInfo = r.Items.Select(item => new
{
IdItemType = item.ItemTypeId
}).ToList()
})
.ToList();
var result = rawData
.Select(r => new
{
r.Id,
r.ReceiptInfo,
ItemInfo = r.RawItemInfo
.GroupBy(item => item.ItemTypeId)
.Select(group => new
{
IdItemType = group.Key,
AmountOfItems = group.Count(),
}).ToList()
});
英文:
In case of EF Core 3.1, you have to postprocess loaded detail items on the client side:
var rawData = _databaseContext.Receipts.Select(r => new
{
r.Id,
r.ReceiptInfo,
RawItemInfo = r.Items.Select(item => new
{
IdItemType = item.ItemTypeId
}).ToList()
})
.ToList();
var result = rawData
.Select(r => new
{
r.Id,
r.ReceiptInfo,
ItemInfo = r.RawItemInfo
.GroupBy(item => item.ItemTypeId)
.Select(group => new
{
IdItemType = group.Key,
AmountOfItems = group.Count(),
}).ToList()
});
答案2
得分: 1
如您所见,EFC 7中的GroupBy
支持已经大幅改进。EFC 3仅支持在查询根部分使用聚合操作的GroupBy。
因此,要在EFC 3中运行它,您需要将查询强制转换为受支持的形式。为了获得相同的分组级别,查询从Items
开始,对三个元素进行分组和聚合,而不是两个:
var result = _databaseContext.Items
.GroupBy(item => new { item.ReceiptId, item.Receipt.ReceiptInfo, item.ItemTypeId })
.Select(group => new
{
Id = group.Key.ReceiptId,
ReceiptInfo = group.Key.ReceiptInfo,
IdItemType = group.Key.ItemTypeId,
NrOfItems = group.Count(),
})
这将返回与原始查询相同的数据,并在数据库中进行数据的聚合。要获得相同的结果形状,需要在内存中进行一些后处理(即在调用AsEnumerable()
后):
.AsEnumerable()
.GroupBy(x => new { x.Id, x.Receipt.ReceiptInfo })
.Select(g => new
{
g.Key.Id,
g.Key.ReceiptInfo,
ItemInfo = g.Select(x => new { x.IdItemType, x.NrOfItems })
});
这需要添加一个名为Item.Receipt
的导航属性。
英文:
As you see, GroupBy
support has improved drastically in EFC 7. EFC 3 only supports GroupBy with aggregates at the query root.
Therefore, to make it run in EFC 3 you need to force the query into the supported shape. To get the same grouping level, the query starts at Items
and groups + aggregates once over three elements instead of two:
var result = _databaseContext.Items
.GroupBy(item => new { item.ReceiptId, item.Receipt.ReceiptInfo, item.ItemTypeId })
.Select(group => new
{
Id = group.Key.ReceiptId,
ReceiptInfo = group.Key.ReceiptInfo,
IdItemType = group.Key.ItemTypeId,
NrOfItems = group.Count(),
})
That returns the same data as the original query and does the reduction of data (aggregate) in the database. To get the same result shape, it needs some post-processing in-memory (i.e. after calling AsEnumerable()
):
.AsEnumerable()
.GroupBy(x => new { x.Id, x.Receipt.ReceiptInfo })
.Select(g => new
{
g.Key.Id,
g.Key.ReceiptInfo,
ItemInfo = g.Select(x => new { x.IdItemType, x.NrOfItems })
});
This requires adding a navigation property Item.Receipt
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论