Luk*_* Z. 5 linq asp.net entity-framework entity-framework-core asp.net-core
我将从 PHP w/ MySQL 转向 ASP .NET Core。
问题:
为了便于说明,假设有以下两个表:
T: {ID, Description, FK}和States: {ID, ID_T, Time, State}。它们之间是1:n的关系(ID_T参考文献T.ID)。
T我需要具有某个特定值FK(可以说)的所有记录1以及相关的最新记录States(如果有)。
从 SQL 的角度来说,可以写成:
SELECT T.ID, T.Description, COALESCE(s.State, 0) AS 'State' FROM T
LEFT JOIN (
SELECT ID_T, MAX(Time) AS 'Time'
FROM States
GROUP BY ID_T
) AS sub ON T.ID = sub.ID_T
LEFT JOIN States AS s ON T.ID = s.ID_T AND sub.Time = s.Time
WHERE FK = 1
Run Code Online (Sandbox Code Playgroud)
我正在努力在 LINQ(或 Fluent API)中编写高效的等效查询。到目前为止我得到的最好的工作解决方案是:
from t in _context.T
where t.FK == 1
join s in _context.States on t.ID equals o.ID_T into _s
from s in _s.DefaultIfEmpty()
let x = new
{
id = t.ID,
time = s == null ? null : (DateTime?)s.Time,
state = s == null ? false : s.State
}
group x by x.id into x
select x.OrderByDescending(g => g.time).First();
Run Code Online (Sandbox Code Playgroud)
当我在执行时在输出窗口中检查生成的 SQL 查询时,它就像:
SELECT [t].[ID], [t].[Description], [t].[FK], [s].[ID], [s].[ID_T], [s].[Time], [s].[State]
FROM [T] AS [t]
LEFT JOIN [States] AS [s] ON [T].[ID] = [s].[ID_T]
WHERE [t].[FK] = 1
ORDER BY [t].[ID]
Run Code Online (Sandbox Code Playgroud)
它不仅选择了比我需要的更多的列(在实际方案中,有更多的列)。查询中没有分组,因此我认为它从数据库中选择所有内容(并且States将会很大),并且分组/过滤发生在数据库外部。
问题:
你会怎么办?
好的。阅读这篇文章(现在已经快一年了)、Smit 对原始问题的评论和其他来源,似乎EF Core 还没有真正做好生产准备。它无法将分组转换为 SQL,因此它是在客户端执行的,这可能(在我的情况下)是一个严重的问题。它对应于观察到的行为(生成的 SQL 查询不进行分组并选择所有组中的所有内容)。在 Linqpad 中尝试 LINQ 查询,它总是会转换为单个 SQL 查询。
阅读本文后我已降级到 EF6。它需要对我的模型代码和一些查询进行一些更改。在我原来的 LINQ 查询中更改为 后,.First()它.FirstOrDefault()工作正常并转换为仅选择所需列的单个 SQL 查询。不过,生成的查询比需要的要复杂得多。
使用 NetMage 的答案中的查询(经过小修改后),它会生成一个几乎与我自己的原始 SQL 查询相同的 SQL 查询(只有一个比 更复杂的构造COALESCE)。
var latestState = from s in _context.States
group s by s.ID_T into sg
select new { ID = sg.Key, Time = sg.Time.Max() };
var ans = from t in _context.T
where t.FK == 1
join sub in latestState on t.ID equals sub.ID into subj
from sub in subj.DefaultIfEmpty()
join s in _context.States
on new { ID_T = t.ID, sub.Time } equals new { s.ID_T, s.Time }
into sj
from s in sj.DefaultIfEmpty()
select new { t.ID, t.Description, State = (s == null ? false : s.State) };
Run Code Online (Sandbox Code Playgroud)
在 LINQ 中,它不像我原来的 SQL 查询那么优雅,但在语义上是相同的,并且它在数据库端执行或多或少相同的操作。
在 EF6 中,使用任意原始 SQL 查询和数据库视图也更加方便。
这种方法的最大缺点是必须针对完整的 .NET 框架,EF6 与 .NET Core 不兼容。
| 归档时间: |
|
| 查看次数: |
3234 次 |
| 最近记录: |