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 扩展方法,以便您执行正确的查询并返回结果。