2010-11-20 185 views
6

Esta es una pregunta relacionada con C# MVC2 Jqgrid - what is the correct way to do server side paging? donde pregunté y encontré cómo mejorar el rendimiento de una consulta en una tabla con 2000 o más filas. El rendimiento se mejoró de 10 segundos a 1 segundo.C# Entity Framework + Linq: ¿cómo acelerar una consulta lenta?

Ahora estoy tratando de realizar exactamente la misma consulta donde la tabla tiene 20,000 filas, y la consulta toma 30 segundos. ¿Cómo puedo mejorarlo más? 20 mil filas todavía no es un número enorme en absoluto.

Algunas ideas posibles que tengo son:

  • Se puede mejorarse de-normalizador para eliminar une y resume
  • Hacer una vista y consulta que en lugar de consultar y unirse a la mesa de
  • Don 'consultar toda la tabla, hacer que el usuario seleccione primero algún filtro (por ejemplo, un filtro A | B | C .. etc.)
  • Agregar más índices a las tablas
  • ¿Algo más?

Esta es la acción MVC que tarda 30 segundos para 20 mil filas: (los parámetros son suministrados por jqGrid, donde SIDX = qué columna especie, y sord = el orden de clasificación)

public ActionResult GetProductList(int page, int rows, string sidx, string sord, 
string searchOper, string searchField, string searchString) 
{ 
    if (sidx == "Id") { sidx = "ProductCode"; } 
    var pagedData = _productService.GetPaged(sidx, sord, page, rows); 
    var model = (from p in pagedData.Page<Product>() 
      select new 
      { 
       p.Id, p.ProductCode, p.ProductDescription, 
       Barcode = p.Barcode ?? string.Empty, 
       UnitOfMeasure = p.UnitOfMeasure != null ? p.UnitOfMeasure.Name : "", 
       p.PackSize, 
       AllocatedQty = p.WarehouseProducts.Sum(wp => wp.AllocatedQuantity), 
       QtyOnHand = p.WarehouseProducts.Sum(wp => wp.OnHandQuantity) 
      }); 

    var jsonData = new 
    { 
     Total = pagedData.TotalPages, Page = pagedData.PageNumber, 
     Records = pagedData.RecordCount, Rows = model 
    }; 

    return Json(jsonData, JsonRequestBehavior.AllowGet); 
} 

ProductService .GetPaged() llama ProductRepository.GetPaged que exige genericRepository.GetPaged() que hace esto:

public ListPage GetPaged(string sidx, string sord, int page, int rows) 
{ 
    var list = GetQuery().OrderBy(sidx + " " + sord); 
    int totalRecords = list.Count(); 

    var listPage = new ListPage 
    { 
     TotalPages = (totalRecords + rows - 1)/rows, 
     PageNumber = page, 
     RecordCount = totalRecords, 
    }; 

    listPage.SetPageData(list 
     .Skip((page > 0 ? page - 1 : 0) * rows) 
     .Take(rows).AsQueryable()); 

    return listPage; 
} 

La cláusula .OrderBy() utiliza LinqExtensions para que pueda pasar en una cadena en lugar de un predicado - podría th ¿Lo está ralentizando?

Y finalmente ListPage es sólo una clase para envolver convenientemente las propiedades que jqGrid necesidades de paginación:

public class ListPage 
{ 
    private IQueryable _data; 
    public int TotalPages { get; set; } 
    public int PageNumber { get; set; } 
    public int RecordCount { get; set; } 

    public void SetPageData<T>(IQueryable<T> data) 
    { 
     _data = data; 
    } 

    public IQueryable<T> Page<T>() 
    { 
     return (IQueryable<T>)_data; 
    } 
} 

GetQuery es:

public IQueryable<T> GetQuery() 
{ 
    return ObjectSet.AsQueryable(); 
} 

El método personalizado .OrderBy consta de estos dos métodos :

public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, 
    string ordering, params object[] values) 
{ 
    return (IQueryable<T>)OrderBy((IQueryable)source, ordering, values); 
} 

public static IQueryable OrderBy(this IQueryable source, string ordering, 
    params object[] values) 
{ 
    if (source == null) throw new ArgumentNullException("source"); 
    if (ordering == null) throw new ArgumentNullException("ordering"); 
    ParameterExpression[] parameters = new ParameterExpression[] { 
     Expression.Parameter(source.ElementType, "") }; 
    ExpressionParser parser = new ExpressionParser(parameters, ordering, values); 
    IEnumerable<DynamicOrdering> orderings = parser.ParseOrdering(); 
    Expression queryExpr = source.Expression; 
    string methodAsc = "OrderBy"; 
    string methodDesc = "OrderByDescending"; 
    foreach (DynamicOrdering o in orderings) 
    { 
     queryExpr = Expression.Call(
      typeof(Queryable), o.Ascending ? methodAsc : methodDesc, 
      new Type[] { source.ElementType, o.Selector.Type }, 
      queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters))); 
     methodAsc = "ThenBy"; 
     methodDesc = "ThenByDescending"; 
    } 
    return source.Provider.CreateQuery(queryExpr); 
} 
+0

Con la edición, estoy muy confundido en cuanto a cómo salta de nuevo al código genérico, cuando ese OrderBy usa código no genérico ... ¿puedes aclarar? ¿Me estoy perdiendo algo obvio? –

+0

Lo siento, no estoy seguro de lo que está preguntando: este 'OrderBy()' es un método de extensión definido en otra clase. –

+0

Bueno, 'GetQuery()' va a devolver 'IQueryable ' (genérico); 'GetQuery(). OrderBy (sidx +" "+ sord)' (utilizando el método que muestra) será 'IQueryable' (no genérico). AFAIK no hay 'Skip (...)' etc * definido * para lo no genérico ... ¿no está seguro de cómo va a funcionar? –

Respuesta

6

El bit que me preocupa es:

.Take(rows).AsQueryable() 

el hecho de que se necesitaba para agregar AsQueryable() me sugiere que es actualmente IEnumerable<T>, lo que significa que podría estar haciendo la paginación en el lado equivocado de la consulta (traer de vuelta manera demasiados datos sobre la red) Sin GetQuery() y el OrderBy() personalizado, es difícil estar seguro, pero como siempre, lo primero que hay que hacer es perfilar la consulta mediante un seguimiento. Vea qué consulta se ejecuta y qué datos se devuelven. EFProf podría hacer que esto sea fácil, pero probablemente una traza de SQL sea suficiente.

+1

+1 - también hay un poco conocido 'ObjectQuery ' ext meth llamado '.ToTraceString()' que muestra la consulta SQL para ser ejecutada. Muy útil para iniciar sesión. – RPM1984

+0

Lo siento, lo eché de menos, 'GetQuery()' es un trazador de líneas: 'return ObjectSet.AsQueryable();'. Editaré y agregaré el código para OrderBy() - No sé de dónde proviene, posiblemente aquí: linqextensions.codeplex.com/ –

+0

@JK - para ser honesto, podría ser suficiente que simplemente compruebe si el El código funciona si elimina '.AsQueryable()' –

Cuestiones relacionadas