Func 委托导致 LINQ-to-Entities 拉回整个 table
Func Delegates cause LINQ-to-Entities to pull back the entire table
将 Func<> 作为 Where/Count 过滤器传递会导致 LINQ 撤回整个 table。这是一个简单的例子。
pdx.Database.Log = strWriter1.Write;
totalCount = pdx.Principals.Count(x => x.PrincipalNumber.ToLower().Contains("42"));
看日志我明白了
SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1]
FROM [Dealer].[Principal] AS [Extent1]
WHERE LOWER([Extent1].[PrincipalNumber]) LIKE N'%42%'
) AS [GroupBy1]
没有拉满table。很简单。现在让我们将该 lambda 分配给 Func<>
pdx.Database.Log = strWriter2.Write;
Func<Principal, bool> filter = (x => x.PrincipalNumber.ToLower().Contains("42"));
totalCount = pdx.Principals.Count(filter);
日志显示它正在拉下整个 table。
SELECT
[Extent1].[PrincipalNumber] AS [PrincipalNumber],
[Extent1].[Id] AS [Id],
[Extent1].[CompanyName] AS [CompanyName],
...
[Extent1].[DistrictSalesManagerId] AS [DistrictSalesManagerId]
FROM [Dealer].[Principal] AS [Extent1]
这对性能来说非常糟糕。我有执行 LINQ 查询的函数。我想将 lambda 过滤器传递给这些函数,以便我可以过滤各种东西,但显然我不能将 lambda 作为 Func<>s 传递,因为它会降低性能。我有什么选择?
我想做什么...
public IEnumerable<DealerInfo> GetMyPage(Func<Principal, bool> filter, int pageNumber, int pageSize, out int totalCount)
{
List<DealerInfo> dealers;
using (MyContext pdx = new MyContext())
{
totalCount = pdx.Principals.Count(filter);
// More LINQ stuff here, but UGH the performance...
}
}
您实际上需要传递 Expression<Func<TSrource,T>>
,Linq to Entities 无法将 Func<T>
转换为 sql,请将签名更改为:
public IEnumerable<DealerInfo> GetMyPage(Expression<Func<Principal, bool>> filter, int pageNumber, int pageSize, out int totalCount)
{
List<DealerInfo> dealers;
using (MyContext pdx = new MyContext())
{
totalCount = pdx.Principals.Count(filter);
// More LINQ stuff here, but UGH the performance...
}
}
当你在Count
方法中传递Func<T,TResult>>
作为参数时,它会调用内存集合中IEnumerable<T>
的Count方法扩展方法,所以这导致了整个table 数据首先加载到内存中,然后当所有数据加载到内存中时执行计数委托,并在内存中执行提供的委托调用,同时传递 Expression<Func<T>>
作为参数将使其翻译语句如果可能,请正确使用 sql,然后调用 IQueryable<T>
的 Count 扩展方法,以便您执行正确的查询并返回结果。
将 Func<> 作为 Where/Count 过滤器传递会导致 LINQ 撤回整个 table。这是一个简单的例子。
pdx.Database.Log = strWriter1.Write;
totalCount = pdx.Principals.Count(x => x.PrincipalNumber.ToLower().Contains("42"));
看日志我明白了
SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1]
FROM [Dealer].[Principal] AS [Extent1]
WHERE LOWER([Extent1].[PrincipalNumber]) LIKE N'%42%'
) AS [GroupBy1]
没有拉满table。很简单。现在让我们将该 lambda 分配给 Func<>
pdx.Database.Log = strWriter2.Write;
Func<Principal, bool> filter = (x => x.PrincipalNumber.ToLower().Contains("42"));
totalCount = pdx.Principals.Count(filter);
日志显示它正在拉下整个 table。
SELECT
[Extent1].[PrincipalNumber] AS [PrincipalNumber],
[Extent1].[Id] AS [Id],
[Extent1].[CompanyName] AS [CompanyName],
...
[Extent1].[DistrictSalesManagerId] AS [DistrictSalesManagerId]
FROM [Dealer].[Principal] AS [Extent1]
这对性能来说非常糟糕。我有执行 LINQ 查询的函数。我想将 lambda 过滤器传递给这些函数,以便我可以过滤各种东西,但显然我不能将 lambda 作为 Func<>s 传递,因为它会降低性能。我有什么选择?
我想做什么...
public IEnumerable<DealerInfo> GetMyPage(Func<Principal, bool> filter, int pageNumber, int pageSize, out int totalCount)
{
List<DealerInfo> dealers;
using (MyContext pdx = new MyContext())
{
totalCount = pdx.Principals.Count(filter);
// More LINQ stuff here, but UGH the performance...
}
}
您实际上需要传递 Expression<Func<TSrource,T>>
,Linq to Entities 无法将 Func<T>
转换为 sql,请将签名更改为:
public IEnumerable<DealerInfo> GetMyPage(Expression<Func<Principal, bool>> filter, int pageNumber, int pageSize, out int totalCount)
{
List<DealerInfo> dealers;
using (MyContext pdx = new MyContext())
{
totalCount = pdx.Principals.Count(filter);
// More LINQ stuff here, but UGH the performance...
}
}
当你在Count
方法中传递Func<T,TResult>>
作为参数时,它会调用内存集合中IEnumerable<T>
的Count方法扩展方法,所以这导致了整个table 数据首先加载到内存中,然后当所有数据加载到内存中时执行计数委托,并在内存中执行提供的委托调用,同时传递 Expression<Func<T>>
作为参数将使其翻译语句如果可能,请正确使用 sql,然后调用 IQueryable<T>
的 Count 扩展方法,以便您执行正确的查询并返回结果。