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);
}
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? –
Lo siento, no estoy seguro de lo que está preguntando: este 'OrderBy()' es un método de extensión definido en otra clase. –
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? –