查询List.Contains()没有缓存

时间:2014-08-07 21:06:47

标签: c# performance linq entity-framework ienumerable

我正在尝试找到一种“缓存”以下查询的方法,因为它的编译增加了我的总渲染时间(这在ASP.NET MVC 5中使用EF 6.1)从<50ms到~700ms:

var revisions = from i in items
                from r in i.Revisions
                where cultures.Contains(r.Culture.CultureCode)
                group r by new { r.ItemId, r.Culture } into g
                select g.OrderByDescending(r => r.DateCreated).FirstOrDefault();

cultures目前是List<string>,通常包含1-3项。

据我所知,IEnumerable.Contains无法缓存,因为在编译时不知道长度,但我也尝试使用一个没有帮助的数组。

更新:以下查询错误,因为它会进行“AND”组合,并且只要请求两种文化就不会返回任何内容。
目前我使用以下内容,这给了我&lt; 50ms渲染时间,但对我来说似乎是“hacky”:

var revisions = items.SelectMany(i => i.Revisions);
foreach (var culture in cultures)
    revisions = revisions.Where(r => r.Culture.CultureCode == culture);

var result = from r in revisions
             group r by new { r.ItemId, r.Culture } into g
             select g.OrderByDescending(r => r.DateCreated).FirstOrDefault();

如果没有“构建自定义查询”,有没有办法获得相同的结果?

有没有办法编写此查询,以便由EF缓存?

我真的不关心〜第一次点击的~700ms“,但这个查询几乎每一页都会执行(因为它加载页面的文本内容) - 有时甚至多次 - 并且会减少整个网站表现显着......

P.S。:我也愿意把查询写得完全不同 我得到一个IQueryable<T>的项目和带有文化的列表来获取每个项目。然后,我需要文化要求的每个项目的最新版本。

多种文化实际上仅用作“后备” - 所以,如果我得到{ "de-AT", "de", "en" },我首先搜索结果以获得de-AT的修订,然后de,最后en并返回找到的第一个。
如果这已经可以“在数据库中”完成,那将是完美的,但我不知道如何在SQL中工作(“文化树”也保存在数据库中并在其他地方查询/缓存,因此它可能是“接合“)。

2 个答案:

答案 0 :(得分:1)

现在我使用LINQkit解决了它,但我仍然希望能够使用Contains

var revisions = itemQuery.SelectMany(i => i.Revisions
    .Where(r => r.State == ContentRevisionState.Published).OfType<R>());

revisions = revisions.ForList(cultures, 
    (p, c) => p.Or(r => r.CultureCode == c.CultureCode));

revisions = revisions.OrderByDescending(r => r.Culture.Position)
    .ThenByDescending(r => r.DateCreated)
    .Include(r => r.Author).Include(r => r.Culture);

var revision = await revisions.FirstOrDefaultAsync();

public static IQueryable<T> ForList<T, K>(this IQueryable<T> items, List<K> list,
    Func<Expression<Func<T, bool>>, K, Expression<Func<T, bool>>> condition)
{
    var predicate = PredicateBuilder.False<T>();
    foreach (var item in list)
    {
        var tmp = item; // local copy for deferred execution
        predicate = condition(predicate, tmp);
    }
    return items.AsExpandable().Where(predicate);
}

答案 1 :(得分:0)

修改

您可能想要预编译查询。有关预编译查询的更多信息:

http://msdn.microsoft.com/en-us/magazine/ee336024.aspx


您只需将查询存储在静态变量中:

private static var query = from i in items
                           from r in i.Revisions
                           where cultures.Contains(r.Culture.CultureCode)
                           group r by new { r.ItemId, r.Culture } into g
                           select g.OrderByDescending(r => r.DateCreated);

然后每次调用它以获得结果而不会导致重新编译:

var revisions = query.FirstOrDefault();

您可以使用此机制获得相同的内容:

private static var query = null;

...
...

if (query == null)
{
    query = ... // initialize it here
}

这样,您也不需要所有引用都是静态的。