在Entity Framework中优化LINQ Any()调用

时间:2011-01-18 09:38:00

标签: linq performance entity-framework

在对基于Entity Framework 4.0的数据库层进行概要分析后,我发现主要的性能罪人是一个简单的LINQ Any(),用于检查数据库中是否已存在实体。 Any()检查比保存实体的速度慢几个数量级。 数据库中的行数相对较少,正在检查的列已编制索引。

我使用以下LINQ来检查是否存在设置组:

from sg in context.SettingGroups
where sg.Group.Equals(settingGroup) && sg.Category.Equals(settingCategory)
select sg).Any()

这会生成以下SQL(另外我的SQL分析器声称查询执行了两次):

exec sp_executesql N'SELECT 
CASE WHEN ( EXISTS (SELECT 
    1 AS [C1]
    FROM [dbo].[SettingGroups] AS [Extent1]
    WHERE ([Extent1].[Group] = @p__linq__0) AND ([Extent1].[Category] = @p__linq__1)
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT 
    1 AS [C1]
    FROM [dbo].[SettingGroups] AS [Extent2]
    WHERE ([Extent2].[Group] = @p__linq__0) AND ([Extent2].[Category] = @p__linq__1)
)) THEN cast(0 as bit) END AS [C1]
FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]',N'@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000)',@p__linq__0=N'Cleanup',@p__linq__1=N'Mediator'

现在我只能考虑创建存储过程来解决这个问题,但我当然更愿意将代码保存在LINQ中。

有没有办法让这种“存在”检查更快地用EF运行?

我应该提一下,我也在n层架构中使用自跟踪实体。在某些情况下,某些实体的ChangeTracker状态设置为“已添加”,即使它们已存在于数据库中。这就是为什么我使用检查来相应地更改ChangeTracker状态,如果更新数据库导致插入失败异常。

4 个答案:

答案 0 :(得分:1)

尝试通过Group& amp;添加索引到数据库表“SettingGroups”。分类

BTW,这会产生类似的sql吗?

var ok = context.SettingGroups.Any(sg => sg.Group==settingGroup && sg.Category==settingCategory);

答案 1 :(得分:0)

我知道这听起来很悲惨,但是如果你使用Count而不是Any会怎么样?

答案 2 :(得分:0)

您是否已经分析了执行生成的select语句的时间与执行select期望/喜欢生成的时间的关系?它可能没有它看起来那么糟糕。

部分

 SELECT 
 1 AS [C1]
 FROM [dbo].[SettingGroups] AS [Extent1]
 WHERE ([Extent1].[Group] = @p__linq__0) AND ([Extent1].[Category] = @p__linq__1)

可能接近你期望的产量。查询优化器很可能会意识到第二个查询与第一个查询相同,因此它可能会为整个查询添加很少的时间。

答案 3 :(得分:0)

问题是实体框架(至少EF4)正在生成愚蠢的SQL。以下代码似乎可以轻松地生成不错的SQL。

public static class LinqExt 
{
    public static bool BetterAny<T>( this IQueryable<T> queryable, Expression<Func<T, bool>> predicate)
    {
        return queryable.Where(predicate).Select(x => (int?)1).FirstOrDefault().HasValue;
    }

    public static bool BetterAny<T>( this IQueryable<T> queryable)
    {
        return queryable.Select(x => (int?)1).FirstOrDefault().HasValue;
    }

}

然后你可以这样做:

(from sg in context.SettingGroups
where sg.Group.Equals(settingGroup) && sg.Category.Equals(settingCategory)
select sg).BetterAny()

甚至:

context.SettingGroups.BetterAny(sg => sg.Group.Equals(settingGroup) && sg.Category.Equals(settingCategory));