c# - C# 实体框架按 Tie Breaker 分组
问题描述
我有一个Person
像这样定义的类:
public class Person {
string Name; // "Joe", "Alex", etc.
string State; // "Minnesota", "Texas", etc.
int Age; // "12", "23", etc.
}
我使用 PostgresQL 作为带有 EF Core 的后备数据库,将person
表绑定到Person
类。
我想执行一个查询,返回List<Person>
每个州最年长的人。例如,如果我有:
Person p1 = new{
Name = "Joe",
State = "Minnesota",
Age = 34
};
Person p2 = new{
Name = "Alex",
State = "Minnesota",
Age = 55
};
Person p3 = new{
Name = "George",
State = "Texas",
Age = 62
}
我想执行一个返回p2
and的查询p3
(结果列表的顺序p2
和p3
内部的顺序无关紧要)。
现在,我正在使用以下查询:
List<Person> oldestByState = await _context.Person.GroupBy(x => x.State).Select(x => x.OrderByDescending(y => y.Age).First()).ToListAsync();
问题在于 C# 不知道如何将其转换为 SQL。我收到一个x.OrderByDescending(y => y.Age)
无法翻译的错误。
我知道快速解决方法是在客户端上进行分组,但我更愿意通过修复 Linq 查询来在数据库上运行查询。
我应该使用什么查询来返回与 Linq 到 PostgresQL 转换兼容的每个州中最年长的人的列表?
EF 核心:5.0.7 Npgsql:5.0.6
解决方案
我用 SQL Server 对此进行了测试,从 EF 收到的错误是“First
只能用作最终运算符,请尝试FirstOrDefault
”。使用FirstOrDefault
工作返回预期的最旧行。
await _context.Person
.GroupBy(x => x.State)
.Select(x => x.OrderByDescending(y => y.Age).FirstOrDefault())
.ToListAsync();
如果它仍然不能与 PostgreSQL 一起使用,那么它可能是该提供程序的限制。
编辑:一种可能的解决方法是使用原始 SQL 查询,等待 npgsql 团队的修复。对于我的测试架构有点不同,EF for SQL Server 产生了:
SELECT
[Limit1].[PersonId] AS [PersonId],
[Limit1].[Name] AS [Name],
[Limit1].[State] AS [State],
[Limit1].[Age] AS [Age],
FROM (SELECT DISTINCT
[Extent1].[State] AS [State]
FROM [dbo].[Persons] AS [Extent1] ) AS [Distinct1]
OUTER APPLY (SELECT TOP (1) [Project2].[PersonId] AS [PersonId], [Project2].[Name] AS [Name], [Project2].[Age] AS [Age], [Project2].[State] AS [State]
FROM ( SELECT
[Extent2].[PersonId] AS [PersonId],
[Extent2].[Name] AS [Name],
[Extent2].[Age] AS [Age],
[Extent2].[State] AS [State]
FROM [dbo].[Children] AS [Extent2]
WHERE [Distinct1].[State] = [Extent2].[State]
) AS [Project2]
ORDER BY [Project2].[Age] DESC ) AS [Limit1]
然后执行:
await _context.Person.FromSqlRaw(@" SELECT
[Limit1].[PersonId] AS [PersonId],
[Limit1].[Name] AS [Name],
[Limit1].[State] AS [State],
[Limit1].[Age] AS [Age],
FROM (SELECT DISTINCT
[Extent1].[State] AS [State]
FROM [dbo].[Persons] AS [Extent1] ) AS [Distinct1]
OUTER APPLY (SELECT TOP (1) [Project2].[PersonId] AS [PersonId], [Project2].[Name] AS [Name], [Project2].[Age] AS [Age], [Project2].[State] AS [State]
FROM ( SELECT
[Extent2].[PersonId] AS [PersonId],
[Extent2].[Name] AS [Name],
[Extent2].[Age] AS [Age],
[Extent2].[State] AS [State]
FROM [dbo].[Children] AS [Extent2]
WHERE [Distinct1].[State] = [Extent2].[State]
) AS [Project2]
ORDER BY [Project2].[Age] DESC ) AS [Limit1]").ToListAsync();
像 &*^ 一样丑陋,并且可能与您的架构不匹配,但如果您可以编写一个返回数据的查询,那么只要它返回您想要的实体,EF 应该能够填充它。如果您尝试返回更复杂的对象图,这将更加复杂。(即具有许多列和相关实体的人员)在这些情况下,我建议运行两个查询,一个执行仅返回 PersonId 的分组和选择,然后使用这些 PersonIds 通过 ID 获取相关人员及其相关数据。
推荐阅读
- graph - 扩展对 EWS 基本身份验证的支持
- java - 无法获取 Selenium Java 的对象列表
- jwt - 我可以强制注销或过期 JWT 令牌吗?
- c# - 如何从另一个应用程序的文本框中获取插入符号的位置?(不是坐标,而是文本框内的实际索引)
- github - 如何使用 Github API 打开草稿拉取请求?
- python - CCXT / python - 自定义请求?
- mysql - 在 Go 中创建 MySql 接口的正确方法
- c# - 使用所有用户的另一个列表合并两个单独的用户列表
- python - 如何用 seaborn 绘制阴影误差带?
- .net-core - MassTransit AzureServieBus InvalidOperationException