2016-09-14 16 views
1

我最近一直在使用SQL服务器分析器,并注意到为我认为应该工作的代码生成两个不同查询的奇怪行为。显然,我错了,因此这个问题。为什么实体框架为非常相似的代码创建了不同的SQL查询

让我们从顶部开始。我有一个非常简单的库类,它包含以下几个方法:

public virtual TEntity GetSingle(Func<TEntity, bool> where, bool asNoTracking = true, params Expression<Func<TEntity, object>>[] includedNavigationProperties) 
    { 
     IQueryable<TEntity> dbQuery = this.ResolveIQueryableForType<TEntity>(asNoTracking, includedNavigationProperties); 
     return dbQuery.Where(where).FirstOrDefault(); 
    } 

    public virtual IQueryable<TEntity> AsQueryable(bool asNoTracking = true, params Expression<Func<TEntity, object>>[] includedNavigationProperties) 
    { 
     IQueryable<TEntity> dbQuery = this.ResolveIQueryableForType<TEntity>(asNoTracking, includedNavigationProperties); 

     return dbQuery; 
    } 

    private IQueryable<TEntityType> ResolveIQueryableForType<TEntityType>(bool asNoTracking, params Expression<Func<TEntityType, object>>[] includedNavigationProperties) 
     where TEntityType : class 
    { 
     IQueryable<TEntityType> dbQuery = _context.Set<TEntityType>(); 

     // Apply eager loading 
     if (includedNavigationProperties != null) 
     { 
      foreach (Expression<Func<TEntityType, object>> navigationProperty in includedNavigationProperties) 
      { 
       dbQuery = dbQuery.Include<TEntityType, object>(navigationProperty); 
      } 
     } 

     if (asNoTracking) 
     { 
      return dbQuery.AsNoTracking(); 
     } 
     else 
     { 
      return dbQuery; 
     } 
    } 

在我做这个调用应用。稍后(其中AccessTokenRepository是我的仓库类型的对象):

accessToken = _repository.AccessTokenRepository.AsQueryable().Where(x => x.AccessTokenID == accessTokenId).FirstOrDefault(); 

导致在此查询:

exec sp_executesql N'SELECT TOP (1) 
    [Extent1].[AccessTokenID] AS [AccessTokenID], 
    [Extent1].[IssuedUtc] AS [IssuedUtc], 
    [Extent1].[ExpiresUtc] AS [ExpiresUtc], 
    [Extent1].[ValidForTimeSpan] AS [ValidForTimeSpan], 
    [Extent1].[CreatedDateTime] AS [CreatedDateTime] 
    FROM [dbo].[AccessToken] AS [Extent1] 
    WHERE [Extent1].[AccessTokenID] = @p__linq__0',N'@p__linq__0 uniqueidentifier',@p__linq__0='62A1BE60-3569-4E80-BC8E-FC01B0FFC266' 

但类似的呼吁(我会说应该产生相同的SQL):

accessToken = _repository.AccessTokenRepository.GetSingle(x => x.AccessTokenID == accessTokenId); 

结果:

SELECT 
    [Extent1].[AccessTokenID] AS [AccessTokenID], 
    [Extent1].[IssuedUtc] AS [IssuedUtc], 
    [Extent1].[ExpiresUtc] AS [ExpiresUtc], 
    [Extent1].[ValidForTimeSpan] AS [ValidForTimeSpan], 
    [Extent1].[CreatedDateTime] AS [CreatedDateTime] 
    FROM [dbo].[AccessToken] AS [Extent1] 

和看起来像整个表的负载。有人可以解释这种负载行为的细微差异吗? 谢谢

回答

4

那是因为你的GetSingle方法的第一个参数定义为

Func<TEntity, bool> where 

而不是

Expression<Func<TEntity, bool>> where 

当你传递的是Func<TEntity, bool>(这简直是一个通用的委托)到Where()方法,您打电话Enumerable.Where()(而不是Queryable.Where()),因此将整个DbSet加载到内存 - 并且SQL将不包括WHERE条款。

请参阅What is the difference between IQueryable<T> and IEnumerable<T>?

+0

谢谢!你可能会推荐一些文献/文章,这些文章/文章将EF比“crud”更深入一些,这样我可以在将来避免类似的问题? – neurotix

+1

严格地说,'Func/Expression '的区别更像是一个LINQ问题,而不是EF问题(任何时候你没有通过一个表达式,LINQ提供程序 - EF在你的情况下 - 将无法解释它,唯一可以匹配的重载是Enumerable类中的重载)。还有一件值得一提的事情是,EF不支持'Queryable'导航属性,这意味着每次你将Where()'(或任何其他LINQ方法)追加到它时,它会从数据库中获取整个数据,然后将使用Linq的方法应用到对象('Enumerable') – haim770

2

GetSingle方法需要Func<>作为参数,这迫使IQueryable<>强制转换为IEnumerable<>,导致查询被“完成”(查询执行将被创建,直到收集是一个表达式决定的形式下载)以及每个后续操作在内存中执行。您必须使用Expression<Func<>>,以便引擎可以正确分析表达式树并生成正确的查询。

相关问题