只有LINQ to Entities中的排序输入才支持'Skip'方法。必须在方法'Skip'之前调用'OrderBy'方法

时间:2014-02-11 15:24:02

标签: c# linq entity-framework

在连接有DbContext Visual Studio 2013 Update 1 中使用 Entity Framework 6.0.2 .NET 4.5.1 到SQL Server:

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

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 ,那么为什么在执行{{{{}}之前我需要执行OrderBy 1}}(我以为我是?)...

  2. 我假设它不会变成SQL查询并执行,直到我枚举结果,这就是为什么我等到最后一行返回结果 AsEnumerable()< / strong>即可。这是正确的做法吗?

  3. 如果我必须在执行SkipSkip之前枚举结果,这将如何影响效果?显然,我想让SQL Server完成繁重的工作,只返回请求的结果。或者没关系(或者我弄错了)?

1 个答案:

答案 0 :(得分: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之前必须枚举结果,那么这将如何影响性能?

这会破坏性能,因为系统需要移动比添加分页之前更多的数据。