【问题标题】:Linq Performance with predicates带有谓词的 Linq 性能
【发布时间】:2015-04-29 13:22:47
【问题描述】:

我现在正在重构一个项目,页面上有很多非常相似的查询

db.WF_Process.Where(x=>x.WorkflowProcessState == 
(int)WorkflowProcessStateEnum.SubtitleFileVersion && 
x.WorkflowProcessSubState == (int)SubtitleFileProcessEnum.SubtitleQCReferred).Count();

db.WF_Process.Where(x=>x.WorkflowProcessState == 
(int)WorkflowProcessStateEnum.SubtitleFileVersion &&
x.WorkflowProcessSubState == (int)SubtitleFileProcessEnum.QCUserFailed).Count();

这会产生以下运行非常快的 SQL

SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[WF_Process] AS [Extent1]
    WHERE (4 = [Extent1].[WorkflowProcessState]) AND (119 = [Extent1].[WorkflowProcessSubState])
)  AS [GroupBy1]

//用100个不同的枚举值重复上述查询

为了稍微清理一下代码,我将其重构为以下方法

 private int GetSubtitleProcessCount(Func<WF_Process, bool> predicate)
        {
            return
                db.WF_Process.Where(x => x.WorkflowProcessState == (int)WorkflowProcessStateEnum.SubtitleFileVersion)
                    .Where(predicate)
                    .Count();
        }

这样称呼

model.SubtitleQCReferred = GetSubtitleProcessCount(x=> x.WorkflowProcessSubState == (int)SubtitleFileProcessEnum.SubtitleQCReferred);

然而这会产生这个 SQL

SELECT 
[Extent1].[ProcessID] AS [ProcessID], 
[Extent1].[MaterialID] AS [MaterialID], 
[Extent1].[VideoVersionID] AS [VideoVersionID], 
[Extent1].[AudioVersionID] AS [AudioVersionID], 
[Extent1].[TXVersionID] AS [TXVersionID], 
[Extent1].[XMLVersionID] AS [XMLVersionID], 
[Extent1].[SubtitleVersionID] AS [SubtitleVersionID], 
[Extent1].[Progress] AS [Progress], 
[Extent1].[ProcessStatusDescription] AS [ProcessStatusDescription], 
[Extent1].[WorkflowProcessState] AS [WorkflowProcessState], 
[Extent1].[WorkflowProcessSubState] AS [WorkflowProcessSubState], 
[Extent1].[ProcessState] AS [ProcessState], 
[Extent1].[ProcessStateDateLastModified] AS [ProcessStateDateLastModified], 
[Extent1].[DateCreated] AS [DateCreated], 
[Extent1].[DateLastChecked] AS [DateLastChecked], 
[Extent1].[SleepUntil] AS [SleepUntil], 
[Extent1].[LongRunningProcessID] AS [LongRunningProcessID]
FROM [dbo].[WF_Process] AS [Extent1]
WHERE 4 = [Extent1].[WorkflowProcessState]

它检索整个记录集,然后在代码中执行计数。

我假设这与第二个 where 子句有关。这是为什么呢?

有什么方法可以保持我正在使用的模式(稍作修改),以便生成体面的 SQL?

【问题讨论】:

  • 从外观上看,您的代码应该生成正确的 sql 以在 SQL 上而不是在内存中运行计数。您的谓词之一导致它在代码中枚举,因为由于某种原因它无法转换为 sql,但它们看起来应该是有效的。
  • 尝试使用Func&lt;WF_Process&gt; 而不是Expression&lt;Func&lt;WF_Process&gt;&gt;。您使用的是 linq to objects。
  • 哦,我知道为什么。因为Where(Func&lt;&gt;) 是内存扩展。你想要Where(Expression&lt;Func&lt;&gt;&gt;)
  • 出于好奇。请注意为什么会有数百次以这种方式检索的计数,如果它们都匹配同一个字段,或者您给出的两个示例只是巧合?使用 group by 并一次获取所有计数然后将计数从内存集合中分配给模型应该更有效?
  • 那么对 db 的 1 次查询不应该足够吗?哪个按字幕和workflowprocesssubstate分组,并返回workflowprocesssubstate编号和计数?

标签: sql .net sql-server linq


【解决方案1】:

如果您将方法的接受类型更改为Expression&lt;Func&lt;WF_Process, bool&gt;&gt;,它应该正确地将Count 逻辑转换为SQL。

即:

GetSubtitleProcessCount(Expression<Func<WF_Process, bool>> predicate)

The documentation on PredicateBuilder 有更多相关信息。

【讨论】:

  • 谢谢,我吃完午饭回来后会测试并接受
猜你喜欢
  • 2014-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-21
  • 2016-08-04
相关资源
最近更新 更多