web-dev-qa-db-fra.com

Entity Framework / Linq to SQL: Skip & Take

Juste curieux de savoir comment le Skip & Take est censé fonctionner. J'obtiens les résultats que je veux voir du côté client, mais quand je connecte le profileur AnjLab SQL et regarde le SQL qui est en cours d'exécution, il semble qu'il interroge et renvoie l'ensemble entier de lignes à la client.

Revient-il vraiment toutes les lignes, puis trie-t-il et réduit-il les choses avec LINQ côté client?

J'ai essayé de le faire avec Entity Framework et Linq to SQL; les deux semblent avoir le même comportement.

Je ne suis pas sûr que cela fasse une différence, mais j'utilise C # dans VWD 2010.

Un aperçu?

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 résultant (Remarque: j'exclus la requête 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]

Après quelques recherches supplémentaires, j'ai constaté que les éléments suivants fonctionnent comme je m'y attendais:

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 résultant:

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

J'aime vraiment la façon dont fonctionne la première option; Passer une expression lambda pour le tri. Existe-t-il un moyen d'accomplir la même chose dans la syntaxe de commande LINQ to SQL? J'ai essayé d'utiliser qry.OrderBy (sort) .Skip (skipRows) .Take (pageSize), mais cela a fini par me donner les mêmes résultats que mon premier bloc de code. Me fait croire que mes problèmes sont liés à OrderBy.

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

PROBLÈME RÉSOLU

J'ai dû encapsuler la fonction lambda entrante dans Expression:

Expression<Func<Store,string>> sort
43
Sam

Les œuvres suivantes et accomplissent la simplicité que je cherchais:

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;
}

La principale chose qui l'a corrigé pour moi était de changer le paramètre de tri Func en:

Expression<Func<Store, string>> sort
34
Sam

Tant que vous ne le faites pas comme queryable.ToList().Skip(5).Take(10), il ne renverra pas le jeu d'enregistrements entier.

Prendre

Ne faire que Take(10).ToList(), fait un SELECT TOP 10 * FROM.

Ignorer

Skip fonctionne un peu différemment car il n'y a pas de fonction 'LIMIT' dans TSQL. Cependant, il crée une requête SQL qui est basée sur le travail décrit dans ce ScottGu blog post .

Si vous voyez l'ensemble du jeu d'enregistrements renvoyé, c'est probablement parce que vous effectuez une ToList() quelque part trop tôt.

6
Jan Jongboom

Solution Entity Framework 6 ici ...

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

par exemple.

using System.Data.Entity;
....

int skip = 5;
int take = 10;

myQuery.Skip(() => skip).Take(() => take);
2
Mick

J'ai créé une extension simple:

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;
}

Utilisation simple:

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();
}
1
Ali Yousefi

Essaye ça:

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();
}

en vérité, ce dernier .ToList () n'est pas vraiment nécessaire car vous retournez IEnumerable ...

Il y aura 2 appels de base de données, un pour le compte et un lorsque la ToList () est exécutée.

0
Bryce Fischer