Entity Framework / Linq to SQL: Пропустить и взять - PullRequest
43 голосов
/ 06 октября 2010

Просто любопытно, как Skip & Take должны работать.Я получаю результаты, которые хочу увидеть на стороне клиента, но когда я подключаю AnjLab SQL Profiler и смотрю на выполняемый SQL, он выглядит так, как будто он запрашивает и возвращает весь набор строк вclient.

Действительно ли он возвращает все строки, затем сортирует и сужает содержимое с помощью LINQ на стороне клиента?

Я пытался сделать это как с Entity Framework, так и с Linq to SQL;оба, кажется, имеют одинаковое поведение.

Не уверен, что это имеет какое-то значение, но я использую C # в VWD 2010.

Любое понимание?

public IEnumerable<Store> ListStores(Func<Store, string> sort, bool desc, int page, int pageSize, out int totalRecords)
{
    var context = new TectonicEntities();
    totalRecords = context.Stores.Count();
    int skipRows = (page - 1) * pageSize;
    if (desc)
        return context.Stores.OrderByDescending(sort).Skip(skipRows).Take(pageSize).ToList();
    return context.Stores.OrderBy(sort).Skip(skipRows).Take(pageSize).ToList();
}

РезультатSQL (Примечание: я исключаю запрос Count):

SELECT 
[Extent1].[ID] AS [ID], 
[Extent1].[Name] AS [Name], 
[Extent1].[LegalName] AS [LegalName], 
[Extent1].[YearEstablished] AS [YearEstablished], 
[Extent1].[DiskPath] AS [DiskPath], 
[Extent1].[URL] AS [URL], 
[Extent1].[SecureURL] AS [SecureURL], 
[Extent1].[UseSSL] AS [UseSSL]
FROM [dbo].[tec_Stores] AS [Extent1]

После некоторых дальнейших исследований я обнаружил, что следующее работает так, как я ожидал:

public IEnumerable<Store> ListStores(Func<Store, string> sort, bool desc, int page, int pageSize, out int totalRecords)
{
    var context = new TectonicEntities();
    totalRecords = context.Stores.Count();
    int skipRows = (page - 1) * pageSize;           
    var qry = from s in context.Stores orderby s.Name ascending select s;
    return qry.Skip(skipRows).Take(pageSize);           
}

Результирующий SQL:

SELECT TOP (3) 
[Extent1].[ID] AS [ID], 
[Extent1].[Name] AS [Name], 
[Extent1].[LegalName] AS [LegalName], 
[Extent1].[YearEstablished] AS [YearEstablished], 
[Extent1].[DiskPath] AS [DiskPath], 
[Extent1].[URL] AS [URL], 
[Extent1].[SecureURL] AS [SecureURL], 
[Extent1].[UseSSL] AS [UseSSL]
FROM ( SELECT [Extent1].[ID] AS [ID], [Extent1].[Name] AS [Name], [Extent1].[LegalName] AS [LegalName], [Extent1].[YearEstablished] AS [YearEstablished], [Extent1].[DiskPath] AS [DiskPath], [Extent1].[URL] AS [URL], [Extent1].[SecureURL] AS [SecureURL], [Extent1].[UseSSL] AS [UseSSL], row_number() OVER (ORDER BY [Extent1].[Name] ASC) AS [row_number]
    FROM [dbo].[tec_Stores] AS [Extent1]
)  AS [Extent1]
WHERE [Extent1].[row_number] > 3
ORDER BY [Extent1].[Name] ASC

Мне действительно нравится, как работает первый вариант;Передав лямбда-выражение для сортировки.Есть ли способ сделать то же самое в синтаксисе LINQ to SQL orderby?Я попытался использовать qry.OrderBy (sort) .Skip (skipRows) .Take (pageSize), но в итоге я получил те же результаты, что и мой первый блок кода.Приводит меня к мысли, что мои проблемы так или иначе связаны с OrderBy.

===================================

РЕШЕНИЕ ПРОБЛЕМЫ

Пришлось обернуть входящую лямбда-функцию в выражение:

Expression<Func<Store,string>> sort

Ответы [ 5 ]

33 голосов
/ 06 октября 2010

Следующее работает и выполняет простоту, которую я искал:

public IEnumerable<Store> ListStores(Expression<Func<Store, string>> sort, bool desc, int page, int pageSize, out int totalRecords)
{
    List<Store> stores = new List<Store>();
    using (var context = new TectonicEntities())
    {
        totalRecords = context.Stores.Count();
        int skipRows = (page - 1) * pageSize;
        if (desc)
            stores = context.Stores.OrderByDescending(sort).Skip(skipRows).Take(pageSize).ToList();
        else
            stores = context.Stores.OrderBy(sort).Skip(skipRows).Take(pageSize).ToList();
    }
    return stores;
}

Главное, что исправило это для меня, это изменение параметра сортировки Func на:

Expression<Func<Store, string>> sort
5 голосов
/ 06 октября 2010

Пока вы не сделаете это как queryable.ToList().Skip(5).Take(10), он не вернет весь набор записей.

Take

Doing only Take(10).ToList(), SELECT TOP 10 * FROM.

Пропустить

Пропустить работает немного по-другому, потому что в TSQL нет функции LIMIT.Однако он создает SQL-запрос, основанный на работе, описанной в этом сообщении в блоге ScottGu .

Если вы видите весь возвращенный набор записей, вероятно, это потому, что вы делаете ToList()где-то слишком рано.

3 голосов
/ 30 октября 2015

Решение Entity Framework 6 здесь ...

http://anthonychu.ca/post/entity-framework-parameterize-skip-take-queries-sql/

, например

using System.Data.Entity;
....

int skip = 5;
int take = 10;

myQuery.Skip(() => skip).Take(() => take);
1 голос
/ 25 сентября 2016

Я создал простое расширение:

public static IEnumerable<T> SelectPage<T, T2>(this IEnumerable<T> list, Func<T, T2> sortFunc, bool isDescending, int index, int length)
{
    List<T> result = null;
    if (isDescending)
        result = list.OrderByDescending(sortFunc).Skip(index).Take(length).ToList();
    else
        result = list.OrderBy(sortFunc).Skip(index).Take(length).ToList();
    return result;
}

Простое использование:

using (var context = new TransportContext())
{
    var drivers = (from x in context.Drivers where x.TransportId == trasnportId select x).SelectPage(x => x.Id, false, index, length).ToList();
}
0 голосов
/ 06 октября 2010

Попробуйте:

public IEnumerable<Store> ListStores(Func<Store, string> sort, bool desc, int page, int pageSize, out int totalRecords)
{
    var context = new TectonicEntities();
    var results = context.Stores;

    totalRecords = results.Count();
    int skipRows = (page - 1) * pageSize;

    if (desc)
        results = results.OrderByDescending(sort);

    return results.Skip(skipRows).Take(pageSize).ToList();
}

по правде говоря, последний .ToList () на самом деле не нужен, так как вы возвращаете IEnumerable ...

Будет 2 вызова базы данныходин для подсчета и один при выполнении ToList ().

...