2016-09-14 18 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<>>,以便引擎可以正確分析表達式樹並生成正確的查詢。

相關問題