试图在LINQ查询(使用Entityframework)中实现条件创建了奇怪的查询。在某些情况下,即使阈值设置为180秒,这些查询也会超时:
List<LogEntity> dataList = db.LogEntities.Where(x => x.Source == "Source" && (String.IsNullOrEmpty(from) || x.EventDate >= cFrom) && (String.IsNullOrEmpty(to) || x.EventDate <= cTo) && (String.IsNullOrEmpty(uid) || x.DomainUserLogin == uid) && (String.IsNullOrEmpty(cid) || x.CaseReference == cid) && (String.IsNullOrEmpty(searchtext) || x.Message.Contains(searchtext))) .OrderByDescending(y => y.EventDate) .Take(500) .ToList<LogEntity>();
使用一些不太优雅的if语句,我没有任何问题,查询在几秒钟内返回:
IQueryable<LogEntity> data = db.LogEntities.Where(x => x.Source == "Source"); if (!String.IsNullOrEmpty(from)) data = data.Where(x => x.EventDate >= cFrom); if (!String.IsNullOrEmpty(to)) data = data.Where(x => x.EventDate <= cTo); if (!String.IsNullOrEmpty(uid)) data = data.Where(x => x.DomainUserLogin == uid); if (!String.IsNullOrEmpty(cid)) data = data.Where(x => x.CaseReference == cid); if (!String.IsNullOrEmpty(searchtext)) data = data.Where(x => x.Message.Contains(searchtext)); data = data.OrderByDescending(x => x.EventDate).Take(500); List<LogEntity> dataList = data.ToList<LogEntity>();
条件语句都是从查询字符串传递的,这就是为什么它们有时可能带有值而有时却没有值的原因。
使用三元运算符时会出现相同的问题
...Where(x => truth ? x.something == somevalue : x.something == anothervalue)
关于这些内联条件为何表现如此差的情况,是否有合理的解释?
当您在EF数据库上使用LINQ编写查询时,它们看起来很自然,但是在后台,有一个查询转换器将LINQ查询解析并将其分为两部分:一个在sql服务器上执行,另一个在客户端上仅使用LINQ扩展名执行。
当您使用某些查询翻译器无法转换为SQL的表达式(例如某些.NET函数)时,它将最大限度地减少数据过滤,并且最终可能会将整个数据表下载到客户端并对其进行过滤。
在您写的第一个查询中,您使用(String.IsNullOrEmpty(from) || x.EventDate >=cFrom);“来自”是LogEntities的外部元素,翻译器无法对其值以及如何根据记录进行计算不做任何假设。因此,最有可能的是,您只需将完整的LogEntities下载到客户端,然后将其过滤到客户端。如果记录数量巨大,您将收到超时错误。
(String.IsNullOrEmpty(from) || x.EventDate >=cFrom)
在第二个查询中,您加入了简单的表达式Where(x => x.DomainUserLogin ==uid);,这些表达式已明确翻译为sql。因此,您将获得正确的sql查询,该查询可以过滤sql服务器端的大多数记录。
Where(x => x.DomainUserLogin ==uid);
您可以使用SQL事件探查器或VS工具(取决于VS版本,或启用EF中的日志记录来查看已执行的实际查询)。
有关MSDN的一些信息