2010-10-06 23 views
39

Solo curiosidad sobre cómo se supone que Omitir & Take funciona. Obtengo los resultados que quiero ver en el lado del cliente, pero cuando conecto AnjLab SQL Profiler y miro el SQL que se está ejecutando, parece como si estuviera buscando y devolviendo todo el conjunto de filas al cliente.Entity Framework/Linq to SQL: Omitir y tomar

¿Realmente está devolviendo todas las filas y luego ordenando y estrechando cosas con LINQ en el lado del cliente?

He intentado hacerlo con Entity Framework y con Linq to SQL; ambos parecen tener el mismo comportamiento.

No

seguro de que hace alguna diferencia, pero estoy usando C# en VWD 2010.

Cualquier idea?

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 resultante (Nota: Estoy excluyendo la consulta Conde):

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] 

Después de una investigación más profunda, he encontrado que los siguientes trabajos como yo esperaría que:

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 resultante:

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 

Me gusta mucho la forma en que funciona la primera opción; Pasando una expresión lambda para ordenar. ¿Hay alguna forma de lograr lo mismo en la sintaxis LINQ to SQL orderby? Intenté usar qry.OrderBy (sort) .Skip (skipRows) .Take (pageSize), pero eso terminó dándome los mismos resultados que mi primer bloque de código. Me lleva a creer que mis problemas están de alguna manera vinculados a OrderBy.

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

problema resuelto

tuvo que envolver la función lambda entrante en la expresión:

Expression<Func<Store,string>> sort 
+0

puede darnos el código para el tipo func? –

+0

Claro, solo estoy pasando un lambda. Ejemplos: x => x.Name, x => x.LegalName, x => x.YearEstablished.ToString() – Sam

+0

Empezando a pensar que debería pasar una cadena y luego usar una instrucción switch para establecer el parámetro orderby apropiado para el LINQ query :(El primer método era mucho más fresco e implicaba mucho menos código. No puedo entender por qué no funciona correctamente. Sin saber exactamente qué está sucediendo, parece que .OrderBy y .OrderByDescending están desencadenando una recuperación de la base de datos, luego aplicando el ordenamiento, luego omitiendo y tomando. Quizás sea eso ... tal vez OrderBy no sepa cómo convertir x => x.Name en el SQL apropiado para que obtenga el conjunto de resultados y luego aplique el orden y el filtrado. – Sam

Respuesta

29

las siguientes obras y lleva a cabo la simplicidad que estaba buscando:

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

Lo más importante que lo fijó para mí estaba cambiando el parámetro de tipo Func para:

Expression<Func<Store, string>> sort 
+0

Tuve un problema similar. Descubrí en el Analizador de SQL que las consultas eran simplemente SELECCIONAR * DE y lo remonté al hecho de que tal vez este parámetro de Expresión <> faltaba en la llamada. Lo actualicé (estaba usando EF4.1 para referencia) y resolvió mi problema. – Amadiere

+0

¿Alguien sabe por qué este es el caso? Yo tuve exactamente el mismo problema. Pensé que había conservado toda mi expresión como IQueryable sin llamar a ToList ni a ninguna otra cosa que enumerara la expresión. Func causa enumeración mientras que Expression no? –

+0

@BrianSweeney La razón de esto, creo, es que si la consulta de clasificación está ** no ** envuelta en una expresión, entonces linq no puede construir un árbol de expresión para convertirla en sql. Debido a esto, tiene que enumerar la consulta para ordenarla antes de hacer el salto/tomar – Anduril

6

siempre y cuando no lo hace como queryable.ToList().Skip(5).Take(10), no va a volver todo el conjunto de registros.

Take

Haciendo única Take(10).ToList(), hace un SELECT TOP 10 * FROM.

Saltar

Saltar funciona un poco diferente porque no hay ninguna función de 'límite' en TSQL. Sin embargo, crea una consulta SQL que se basa en el trabajo descrito en este ScottGu blog post.

Si ve el conjunto de registros completo devuelto, es probable que sea porque está haciendo un ToList() demasiado pronto.

+0

Ejecutando ToList() al final Ejemplo: db.Stores.OrderBy (x => x.Name) .Skip (5) .Take (5) .ToList() – Sam

+0

Sí, pero anteriormente. ¿Algún otro método está haciendo un ToList() en su conjunto original. –

+0

Código publicado arriba ... – Sam

0

Prueba esto:

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 la verdad, que el pasado .ToList() no es realmente necesario, ya que usted está volviendo IEnumerable ...

Habrá 2 llamadas a la base de datos, una para el conteo y otra cuando se ejecuta ToList().

+0

resultados es un tipo ObjectSet . Los resultados.OrderByDescending (clasificación) devuelve IOrderedEnumerable por lo que no se puede asignar a los resultados. Sin embargo, me diste un par de ideas sobre qué hacer. Volveré a publicar una vez que haya probado. – Sam

+0

eso es lo que todos devuelven. Hasta que especifique un .Select() o .ToList(). –

+0

Correcto, pero el compilador impide "if (desc) results = results.OrderByDescending (sort)" porque los tipos son diferentes. También descubrí que recibo un error cuando uso .Skip sin llamar a .OrderBy primero (ocurre cuando! Desc). Ediciones publicadas en mi publicación original con hallazgos adicionales. – Sam

3

Marco de la entidad solución 6 aquí ...

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

correo .gramo.

using System.Data.Entity; 
.... 

int skip = 5; 
int take = 10; 

myQuery.Skip(() => skip).Take(() => take); 
+1

Creo que malinterpretaste la pregunta. OP nunca menciona la parametrización de 'Omitir' y 'Tomar', solo que no se estaban ejecutando en SQL (debido a un uso incorrecto de 'Func <,>' en 'OrderBy' en lugar de' Expresión > '). Esta podría ser la razón por la que marcó mi pregunta como un duplicado, aunque no son lo mismo. – GWB

0

creé simple extensión:

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

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