1

我有一个复杂的实体框架查询。我的性能瓶颈实际上不是查询数据库,而是将 IQueryable 转换为查询文本。

我的代码是这样的:

var query = context.Hands.Where(...)
if(x)
    query = query.where(...)
....
var result = query.OrderBy(...)
var page = result.skip(500 * pageNumber).Take(500).ToList(); //loong time here, even before calling the DB

do
{
    foreach(var h in page) { ... }

    pageNumber += 1;
    page = result.skip(500 * pageNumber).Take(500).ToList(); //same here
}
while(y)

我能做些什么?我正在使用 DbContext(带有 SQLite),所以我不能使用预编译查询(即使这样,这样的查询构建算法也会很麻烦)。

我基本上需要的是缓存一个“页面”查询并且只更改“跳过”和“获取”参数,而不是每次都从头开始重新编译。

4

1 回答 1

4

你的前提不正确。因为您ToList在查询结束时有一个调用,所以您正在查询您指定的数据库以构建列表。您不再推迟执行。这就是为什么需要这么长时间。您不会花费很长时间来构建查询,而是需要很长时间才能进入数据库并实际执行它。

如果有帮助,您可以使用以下方法为您进行分页。它将推迟获取每一页,直到您请求下一个:

public static IEnumerable<IEnumerable<T>> Paginate<T>(
    this IQueryable<T> query, int pagesize)
{
    int pageNumber = 0;

    var page = query.Take(pagesize).ToList();
    while (page.Any())
    {
        yield return page;
        pageNumber++;
        page = query.Skip(pageNumber * pagesize)
            .Take(pagesize)
            .ToList();
    }
}

所以如果你有这个代码:

var result = query.OrderBy(...);
var pages = result.Paginate();//still haven't hit the database

//each iteration of this loop will go query the DB once to get that page
foreach(var page in pages)
{
    //use page
}

如果您想获得一个IEnumerable<IQueryable<T>>将所有页面作为查询的方式(这意味着您可以在将它们发送到数据库之前向它们添加更多过滤器),那么您遇到的主要问题是您不知道会有多少页面是。您需要实际执行给定的查询才能知道它是否是最后一页。您要么需要像此代码那样随时获取每个页面,要么需要在开始时查询未分页查询的计数(这意味着比您需要的数据库查询多一个)。这样做看起来像:

public static IEnumerable<IQueryable<T>> Paginate<T>(
    this IQueryable<T> query, int pagesize)
{
    //note that this is hitting the DB
    int numPages = (int)Math.Ceiling(query.Count() / (double)pagesize);

    for (int i = 0; i < numPages; i++)
    {
        var page = query.Skip(i * pagesize)
                .Take(pagesize);
        yield return page;
    }
}
于 2013-07-03T14:45:55.213 回答