在LINQ to Entities中,“Skip”方法仅支持排序输入。在使用“Skip”方法之前,必须先调用“OrderBy”方法。

9
在使用连接到SQL Server的`DbContext`的情况下,在Visual Studio 2013 Update 1中使用Entity Framework 6.0.2和.NET 4.5.1:
我有一个长链的过滤器,基于调用者所需的结果应用到查询中。一切都很好,直到我需要添加分页功能。以下是一个概览:
IQueryable<ProviderWithDistance> results = (from pl in db.ProviderLocations
                                            let distance = pl.Location.Geocode.Distance(_geo)
                                            where pl.Location.Geocode.IsEmpty == false
                                            where distance <= radius * 1609.344
                                            orderby distance
                                            select new ProviderWithDistance() { Provider = pl.Provider, Distance = Math.Round((double)(distance / 1609.344), 1) }).Distinct();

if (gender != null)
{
    results = results.Where(p => p.Provider.Gender == (gender.ToUpper() == "M" ? Gender.Male : Gender.Female));
}

if (type != null)
{
    int providerType;
    if (int.TryParse(type, out providerType))
        results = results.Where(p => p.Provider.ProviderType.Id == providerType);
}

if (newpatients != null && newpatients == true)
{
    results = results.Where(p => p.Provider.ProviderLocations.Any(pl => pl.AcceptingNewPatients == null || pl.AcceptingNewPatients == AcceptingNewPatients.Yes));
}

if (string.IsNullOrEmpty(specialties) == false)
{
    List<int> _ids = specialties.Split(',').Select(int.Parse).ToList();

    results = results.Where(p => p.Provider.Specialties.Any(x => _ids.Contains(x.Id)));
}

if (string.IsNullOrEmpty(degrees) == false)
{
    List<int> _ids = specialties.Split(',').Select(int.Parse).ToList();

    results = results.Where(p => p.Provider.Degrees.Any(x => _ids.Contains(x.Id)));
}

if (string.IsNullOrEmpty(languages) == false)
{
    List<int> _ids = specialties.Split(',').Select(int.Parse).ToList();

    results = results.Where(p => p.Provider.Languages.Any(x => _ids.Contains(x.Id)));
}

if (string.IsNullOrEmpty(keyword) == false)
{
    results = results.Where(p =>
        (p.Provider.FirstName + " " + p.Provider.LastName).Contains(keyword));
}

这是我添加到底部的分页代码(其中skipmax只是int类型的参数)。
if (skip > 0)
    results = results.Skip(skip);

results = results.Take(max);

return new ProviderWithDistanceDto { Locations = results.AsEnumerable() };

现在轮到我问问题了:

  1. 你可以看到,在最初的LINQ查询中,我正在使用orderby,那么为什么它会抱怨我需要在执行Skip之前进行OrderBy呢?(我以为我已经这样做了?)...

  2. 我认为只有当我枚举结果时,它才会被转换为SQL查询并执行,这就是为什么我等到最后一行才返回结果的AsEnumerable()。这是正确的方法吗?

  3. 如果我必须在执行SkipTake之前枚举结果,那么这将如何影响性能?显然,我希望SQL Server进行繁重的工作并仅返回请求的结果。或者这不重要(或者我搞错了)?

1个回答

11

我正在LINQ查询中进行orderby操作,为什么它会抱怨我需要在执行Skip之前进行OrderBy操作(难道我不是这样做的吗?)

你的result起始时是一个正确的有序查询:第一行查询返回的类型是IOrderedQueryable<ProviderWithDistance>,因为你有一个order by子句。然而,在其上添加一个Where使你的查询重新变成了一个普通的IQueryable<ProviderWithDistance>,导致了之后出现的问题。从逻辑上讲,这是相同的事情,但查询定义在内存中的结构表明了不同。

要解决此问题,请从原始查询中删除order by,并在你准备进行分页时添加它,像这样:

    ...
    if (string.IsNullOrEmpty(languages) == false)
        ...
    if (string.IsNullOrEmpty(keyword) == false)
        ...
    result = result.OrderBy(r => r.distance);
只要将订购作为最后一个操作,这应该可以解决运行时问题。
我原本认为只有当我枚举结果时它才不会被转化为 SQL 查询并执行,这就是为什么我等到最后一行才返回结果 AsEnumerable()。这是正确的方法吗?
是的,那是正确的方法。你希望你的 RDBMS 尽可能多地工作,因为在内存中进行分页会使分页失去意义。
如果我必须在进行 Skip 和 Take 之前枚举结果,那会对性能产生什么影响?
这将损害性能,因为添加分页后,系统需要移动比之前更多的数据。

快速评论:如果您正在使用“switch”语句来处理您的sortOrder,请尝试使用一个“default”情况,它将按您选择的字段对结果集进行排序。 - CodeBreaker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接