在 EF Core 中选择分组项
Selecting Grouped Items in EF Core
这在 EF 6.4 中有效:
from a in Addresses
group a by new {a.StreetName, a.StreetNumber} into agrp
where agrp.Count() > 3
from aitem in agrp
select aitem
如果 EF Core 5 我得到:
InvalidOperationException: The LINQ expression 'agrp => agrp' could
not be translated. Either rewrite the query in a form that can be
translated, or switch to client evaluation explicitly by inserting a
call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or
'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for
more information.
为什么?有没有不同的写法?
如果您同意将数据加载到内存中,一个简单的解决方案可能是在 Addresses
:
之后添加 .ToList()
或 .AsEnumerable()
from a in Addresses.ToList() // or .AsEnumerable()
group a by new {a.StreetName, a.StreetNumber} into agrp
where agrp.Count() > 3
from aitem in agrp
select aitem
请注意,这(在 SqlServer 中)转换为:
SELECT [a].[Id], [a].[StreetName], [a].[StreetNumber]
FROM [Addresses] AS [a]
在 EF Core 中,GroupBy
(在许多情况下)未转换为 SQL,但在内存中为 运行。
(为避免不小心将大量数据加载到内存中,EF 将抛出异常,除非调用 .ToList()
或 .AsEnumerable()
以表明这是故意的。)
(...) Since no database structure can represent an IGrouping
, GroupBy
operators have no translation in most cases. When an aggregate operator is applied to each group, which returns a scalar, it can be translated to SQL GROUP BY
in relational databases. (...)
- Complex query operators, GroupBy
该文章还有一个查询示例,该查询转换为 group by
,并在 Count
上进行筛选(包括在下方)。
不幸的是,该示例并未完全涵盖问题中的示例。它不会 return 相关的 Address-objects,只有 group-by Key 和 Count。
var query = from p in context.Set<Post>()
group p by p.AuthorId into g
where g.Count() > 0
orderby g.Key
select new
{
g.Key,
Count = g.Count()
};
SELECT [p].[AuthorId] AS [Key], COUNT(*) AS [Count]
FROM [Posts] AS [p]
GROUP BY [p].[AuthorId]
HAVING COUNT(*) > 0
ORDER BY [p].[AuthorId]
这似乎有效:
var addressGroupQuery = from a in Addresses
group a by new {a.StreetName, a.StreetType} into agrp
where agrp.Count() > 3
select agrp.Max(a => a.AddressID);
var addresses = from a in Addresses
where addressGroupQuery.Contains(a.AddressID)
select a;
请注意,addressGroupQuery 仍然是一个延迟查询(没有 ToList)。也生成一些干净的外观 SQL:
SELECT [a].[AddressID], [a].[City], [a].[Country], [a].[StreetName], [a].[StreetNumber], [a].[StreetType], [a].[UnitNumber]
FROM [Address] AS [a]
WHERE EXISTS (
SELECT 1
FROM [Address] AS [a0]
GROUP BY [a0].[StreetName], [a0].[StreetType]
HAVING (COUNT(*) > 3) AND (MAX([a0].[AddressID]) = [a].[AddressID]))
这在 EF 6.4 中有效:
from a in Addresses
group a by new {a.StreetName, a.StreetNumber} into agrp
where agrp.Count() > 3
from aitem in agrp
select aitem
如果 EF Core 5 我得到:
InvalidOperationException: The LINQ expression 'agrp => agrp' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
为什么?有没有不同的写法?
如果您同意将数据加载到内存中,一个简单的解决方案可能是在 Addresses
:
.ToList()
或 .AsEnumerable()
from a in Addresses.ToList() // or .AsEnumerable()
group a by new {a.StreetName, a.StreetNumber} into agrp
where agrp.Count() > 3
from aitem in agrp
select aitem
请注意,这(在 SqlServer 中)转换为:
SELECT [a].[Id], [a].[StreetName], [a].[StreetNumber]
FROM [Addresses] AS [a]
在 EF Core 中,GroupBy
(在许多情况下)未转换为 SQL,但在内存中为 运行。
(为避免不小心将大量数据加载到内存中,EF 将抛出异常,除非调用 .ToList()
或 .AsEnumerable()
以表明这是故意的。)
(...) Since no database structure can represent an
IGrouping
,GroupBy
operators have no translation in most cases. When an aggregate operator is applied to each group, which returns a scalar, it can be translated to SQLGROUP BY
in relational databases. (...)
- Complex query operators, GroupBy
该文章还有一个查询示例,该查询转换为 group by
,并在 Count
上进行筛选(包括在下方)。
不幸的是,该示例并未完全涵盖问题中的示例。它不会 return 相关的 Address-objects,只有 group-by Key 和 Count。
var query = from p in context.Set<Post>()
group p by p.AuthorId into g
where g.Count() > 0
orderby g.Key
select new
{
g.Key,
Count = g.Count()
};
SELECT [p].[AuthorId] AS [Key], COUNT(*) AS [Count]
FROM [Posts] AS [p]
GROUP BY [p].[AuthorId]
HAVING COUNT(*) > 0
ORDER BY [p].[AuthorId]
这似乎有效:
var addressGroupQuery = from a in Addresses
group a by new {a.StreetName, a.StreetType} into agrp
where agrp.Count() > 3
select agrp.Max(a => a.AddressID);
var addresses = from a in Addresses
where addressGroupQuery.Contains(a.AddressID)
select a;
请注意,addressGroupQuery 仍然是一个延迟查询(没有 ToList)。也生成一些干净的外观 SQL:
SELECT [a].[AddressID], [a].[City], [a].[Country], [a].[StreetName], [a].[StreetNumber], [a].[StreetType], [a].[UnitNumber]
FROM [Address] AS [a]
WHERE EXISTS (
SELECT 1
FROM [Address] AS [a0]
GROUP BY [a0].[StreetName], [a0].[StreetType]
HAVING (COUNT(*) > 3) AND (MAX([a0].[AddressID]) = [a].[AddressID]))