简单的 LINQ 到实体翻译
Posted
技术标签:
【中文标题】简单的 LINQ 到实体翻译【英文标题】:Simple LINQ to Entities translation 【发布时间】:2019-08-15 16:03:15 【问题描述】:在有人将标记视为重复之前,我已经查看了每个人都在做的事情比我尝试的要复杂一些。
所以我正在开发一个数据库,其中有很多数据要检查,而 LINQ 的 Any() 扩展转换为 SQL 不如 SQL 的 Count(1) > 0 快,所以我在写的任何地方:
var someBool = Ctx.SomeEntities.Count(x => x.RelatedEntity.Count(y => y.SomeProperty == SomeValue) > 0) > 0;
在伪中:我的任何实体是否与具有值为 SomeValue 的属性的某个其他实体有关系。
这很好用,而且运行速度很快。但是,它并不完全可读(我有很多,在某些情况下比嵌入更多)所以我想做的就是将其替换为:
var someBool = Ctx.SomeEntities.AnyX(x => x.RelatedEntity.AnyX(y => y.SomeProperty == SomeValue));
与:
public static bool AnyX<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) => source.Count(predicate) > 0;
所以你看我没有做任何 LINQ 无法转换为 SQL 的事情,我没有做任何 LINQ 尚未转换为 SQL 的事情,而只是通过创建一个额外的扩展我得到:
LINQ to Entities 无法识别 Boolean AnyX 等方法...
必须有某种方式来编写我的扩展程序或某种方式告诉 LINQ 只是为了看看代码,你就会发现你可以做到。
【问题讨论】:
你为什么使用 AnyX 而不是 Any? 我看到了两个大问题,首先 EF 处理Expressions
,而不是代表;传递Expression<Func<TSource, bool>>
而不是Func<TSource, bool>
,它需要扩展IQueryable<>
,而不是IEnumerable
。
@Tod 您复制了 Any
的 IEnumerable 版本的代码。 IQueryable 版本不同。 IEnumerable版本,IQueryable版本
在尝试以这种方式“增强”实体框架时,了解扩展 IEnumerable
和 IQueryable
之间的区别至关重要。如果您在IEnumerable
上操作,它将在本地计算机上运行,不会被翻译成 SQL。
见***.com/a/36736907/1625737
【参考方案1】:
不是针对您的具体问题的答案,但我建议您重新考虑如何处理查询。
让我们使用一些更容易理解的描述性名称:是否有任何家庭的居民的名字是“Bobby”?
// your way
Ctx.Households.Count( hh => hh.Residents.Count( r => r.FirstName == "Bobby" ) > 0 ) > 0
糟糕,它倒退了。从居民开始:
Ctx.Residents.Count( r =>
r.FirstName == "Bobby"
&& r.Household != null ) // if needed
> 0;
现在,生成的 SQL 会与下面的明显不同吗?
Ctx.Residents.Any( r => r.FirstName == "Bobby" && r.Household != null)
编辑:
这是一个与你的结论相反的真实 MCVE:
/*
create table TestDatum
(
TestValue nchar(36) not null
)
*/
/*
set nocount on
declare @count int
declare @start datetime
declare @end datetime
set @count = 0
set @start = GETDATE()
while @count < 14000000
begin
insert TestDatum values( CONVERT(nchar(36), NEWID()) )
set @count = @count + 1
if (@count % 100000) = 0
begin
print convert(nvarchar, @count)
end
end
set @end = GETDATE()
select CONVERT(nvarchar, DATEDIFF(ms, @start, @end))
*/
/*
-- "Any" test
declare @startdt datetime, @enddt datetime
set @startdt = GETDATE()
DECLARE @p0 NVarChar(1000) = '%abcdef%'
SELECT
(CASE
WHEN EXISTS(
SELECT NULL AS [EMPTY]
FROM TestDatum AS [t0]
WHERE [t0].TestValue LIKE @p0
) THEN 1
ELSE 0
END) AS [value]
set @enddt = GETDATE()
select DATEDIFF(ms, @startdt, @enddt) -- ~7000ms
*/
/*
-- "Count" test
declare @startdt datetime, @enddt datetime
set @startdt = GETDATE()
-- Region Parameters
DECLARE @p0 NVarChar(1000) = '%abcdef%'
-- EndRegion
SELECT COUNT(*) AS [value]
FROM TestDatum AS [t0]
WHERE [t0].TestValue LIKE @p0
set @enddt = GETDATE()
select DATEDIFF(ms, @startdt, @enddt) -- > 48000ms
*/
【讨论】:
是 SQL 将 any 转换为:CASE WHEN EXISTS( SELECT NULL AS [EMPTY] FROM。Count 就是 COUNT @Tod - 好的,你是说计算所有记录比找到第一条记录时短路更快或更优化? 是的,这正是我要说的。我不知道为什么,我之前在 SO 上发布过,请检查问题中的 cmets 以获取链接。就两种方法的速度而言,一些数据可以进行比较。其他时候计数会快得多。 您能提供一个MCVE 重现此行为吗? 就像我说的,在问题的 cmets 中链接。 ***.com/questions/648795/…以上是关于简单的 LINQ 到实体翻译的主要内容,如果未能解决你的问题,请参考以下文章
LINQ to NHibernate - 如何检测失败的翻译