为什么实体框架为非常相似的代码创建不同的sql查询?
本文关键字:sql 查询 创建 代码 框架 非常 相似 为什么 实体 | 更新日期: 2023-09-27 18:06:55
我最近一直在使用SQL server profiler,并注意到为我认为应该相同的代码生成两个不同查询的奇怪行为。显然,我错了,因此问题。
让我们从头开始。我有一个非常简单的存储库类,它由以下方法组成:
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]
,这看起来像整个表加载。有人能解释一下载荷行为的细微差别吗?谢谢你
这是因为您的GetSingle
方法的第一个参数被定义为
Func<TEntity, bool> where
代替
Expression<Func<TEntity, bool>> where
当你传递的Func<TEntity, bool>
(这只是一个通用的委托)到Where()
方法,你调用Enumerable.Where()
(而不是Queryable.Where()
)因此加载整个DbSet
到内存- SQL将不包括WHERE
子句。
参见IQueryable
您的GetSingle
方法将Func<>
作为参数,这将强制将IQueryable<>
转换为IEnumerable<>
,从而导致查询"完成"(执行的查询将具有由创建的表达式决定的形式,直到集合被下转换),并且每个后续操作都将在内存中执行。您必须使用Expression<Func<>>
,这样引擎才能正确地分析表达式树并生成正确的查询。