为什么Entity Framework为非常相似的代码

时间:2016-09-14 15:37:05

标签: c# .net entity-framework-6

我最近一直在玩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]

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

2 个答案:

答案 0 :(得分: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>?

答案 1 :(得分:2)

您的weather(phoenix,summer,hot). weather(la,summer,warm). weather(phoenix,winter,warm). weather(monteal,winter,warm). 方法将GetSingle作为参数,这会强制Func<>转换为IQueryable<>,从而导致查询完成&#34;已完成&#34 ; (执行的查询将具有由创建的表达式决定的形式,直到集合被转发为止)并且每个后续操作都将在内存中执行。您必须使用IEnumerable<>,以便引擎可以正确分析表达式树并生成正确的查询。