【问题标题】:The method ‘Skip’ is only supported for sorted input in LINQ to Entities. The method ‘OrderBy’ must be called before the method ‘Skip’仅 LINQ to Entities 中的排序输入支持“跳过”方法。必须在方法“Skip”之前调用方法“OrderBy”
【发布时间】:2014-03-09 11:30:30
【问题描述】:

Visual Studio 2013 更新 1 中使用 Entity Framework 6.0.2.NET 4.5.1,并将 DbContext 连接到 SQL服务器:

我有一长串过滤器,我根据调用者的期望结果应用于查询。一切都很好,直到我需要添加分页。先来看看:

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 完成繁重的工作并只返回请求的结果。还是没关系(或者我弄错了)?

【问题讨论】:

    标签: c# linq entity-framework


    【解决方案1】:

    我在初始 LINQ 查询中执行 orderby,为什么它抱怨我需要在执行 Skip 之前执行 OrderBy(我以为我是?)

    您的result 以有序可查询的形式开始正确:第一行查询返回的类型是IOrderedQueryable&lt;ProviderWithDistance&gt;,因为您有一个order by 子句。但是,在其顶部添加Where 会使您的查询再次成为普通的IQueryable&lt;ProviderWithDistance&gt;,从而导致您在路上看到的问题。从逻辑上讲,这是一回事,但内存中查询定义的结构另有含义。

    要解决此问题,请删除原始查询中的 order by,并在准备分页之前添加它,如下所示:

        ...
        if (string.IsNullOrEmpty(languages) == false)
            ...
        if (string.IsNullOrEmpty(keyword) == false)
            ...
        result = result.OrderBy(r => r.distance);
    

    只要排序是最后一个操作,这应该可以解决运行时问题。

    我假设它不会被转换为 SQL 查询并在我枚举结果之前执行,这就是为什么我等到最后一行返回结果AsEnumerable()。这是正确的做法吗?

    是的,这是正确的方法。您希望您的 RDBMS 做尽可能多的工作,因为在内存中进行分页首先会破坏分页的目的。

    如果我必须在执行 Skip and Take 之前枚举结果,这将如何影响性能?

    这会降低性能,因为与添加分页之前相比,您的系统需要移动更多的数据。

    【讨论】:

    • 快速评论:如果您使用“switch”语句来处理您的 sortOrder,请尝试使用“默认”案例,该案例将按您选择的字段对结果集进行排序。
    猜你喜欢
    • 2012-06-28
    • 1970-01-01
    • 2014-05-13
    • 1970-01-01
    • 1970-01-01
    • 2012-07-20
    • 2014-10-05
    • 1970-01-01
    相关资源
    最近更新 更多