简体   繁体   English

缓存/编译复杂的Linq查询(实体框架)

[英]Caching/compiling complex Linq query (Entity Framework)

I have a complex Entity Framework query. 我有一个复杂的实体框架查询。 My performance bottleneck is not actually querying the database, but translating the IQueryable into query text. 我的性能瓶颈实际上并不是查询数据库,而是将IQueryable转换为查询文本。

My code is something like this: 我的代码是这样的:

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)

What can I do? 我能做什么? I am using DbContext (with SQLite), so I can't use precompiled query (and even then, it would be cumbersome with query building algorithm like this). 我正在使用DbContext(使用SQLite),因此我无法使用预编译查询(即便如此,使用这样的查询构建算法也会很麻烦)。

What I basically need, is to cache a "page" query and only change the "skip" and "take" parameters, without recompiling it from the ground up each time. 我基本上需要的是缓存“页面”查询并仅更改“跳过”和“获取”参数,而无需每次从头开始重新编译。

Your premise is incorrect. 你的前提是不正确的。 Because you have a ToList call at the end of your query you are querying the database where you've indicated, to construct the list. 因为您在查询结束时有一个ToList调用,所以您正在查询您指定的数据库,以构建列表。 You're not deferring execution any longer. 你不再推迟执行。 That's why it takes so long. 这就是为什么需要这么长时间。 You aren't spending a long time constructing the query, it's taking a long time to go to the database and actually execute it. 你没有花很长时间构建查询,它需要很长时间才能进入数据库并实际执行它。

If it helps you can use the following method to do the pagination for you. 如果它有帮助您可以使用以下方法为您进行分页。 It will defer fetching each page until you ask for the next one: 它将推迟获取每个页面,直到您要求下一个页面:

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();
    }
}

So if you had this code: 所以如果你有这个代码:

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
}

If you want to get an IEnumerable<IQueryable<T>> in which you have all of the pages as queries (meaning you could add further filters to them before sending them to the database) then the major problem you have is that you don't know how many pages there will be. 如果你想获得一个IEnumerable<IQueryable<T>> ,你将所有页面都作为查询(意味着你可以在将它们发送到数据库之前为它们添加更多过滤器),那么你遇到的主要问题是你没有我知道会有多少页。 You need to actually execute a given query to know if it's the last page or not. 您需要实际执行给定的查询以了解它是否是最后一页。 You either need to fetch each page as you go, as this code does, or you need to query the count of the un-paged query at the start (which means one more DB query than you would otherwise need). 您可能需要像往常一样获取每个页面,或者您需要在开始时查询未分页查询的计数(这意味着比您需要的更多数据库查询)。 Doing that would look like: 这样做看起来像:

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;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM