2010-11-20 4 views
6

Это вопрос, связанный с C# MVC2 Jqgrid - what is the correct way to do server side paging?, где я спросил и нашел, как повысить производительность для запроса в таблице с примерно 2000 строк. Производительность была улучшена с 10 секунд до 1 секунды.C# Entity Framework + Linq - как вы ускоряете медленный запрос?

Теперь я пытаюсь выполнить тот же запрос, где таблица содержит 20 000 строк - и запрос занимает 30 секунд. Как я могу улучшить его дальше? 20 тыс. Рядов по-прежнему невелики.

Некоторые возможные идеи я являются:

  • может быть улучшена за счет денормализацию для удаления соединений и подводит
  • сделать вид и запрос, который вместо того, чтобы запрашивать и присоединение к таблице
  • Дон 't запрашивать всю таблицу, сначала выберите фильтр (например, фильтр A | B | C .. etc)
  • Добавить индексы в таблицы
  • Что-то еще?

Это действие MVC, который занимает 30 секунд, 20 тысяч строк: (параметры снабжены jqgrid, где sidx = какой столбец сортировки, и Sord = порядок сортировки)

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() вызывает ProductRepository.GetPaged, который вызывает genericRepository.GetPaged(), который делает это:

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

The() пункт .OrderBy использует LinqExtensions, так что я могу передать строку вместо предиката - может й замедляет его?

И наконец ListPage это просто класс для удобного подведению свойства, jqgrid потребности пейджинга:

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 является:

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

Обычай метод .OrderBy состоит из этих двух методов :

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

С редактированием я очень смущен относительно того, как он возвращается к универсальному коду, когда этот OrderBy использует не общий код ... вы можете уточнить? я пропущу что-то очевидное? –

+0

Извините, я не уверен, что вы просите - этот 'OrderBy()' - это метод расширения, определенный в другом классе. –

+0

Ну, 'GetQuery()' собирается возвратить 'IQueryable ' (generic); 'GetQuery(). OrderBy (sidx +" "+ sord)' (используя метод, который вы показываете) будет 'IQueryable' (не общий). AFAIK не существует 'Skip (...)' etc * defined * для не-generic ... не уверен, как это будет работать? –

ответ

6

Немного, что касается меня:

.Take(rows).AsQueryable() 

тот факт, что вам нужно добавить AsQueryable() подсказывает мне, что в настоящее время IEnumerable<T> означает, что вы могли бы сделать подкачки на неправильном конце запроса (возвращая путь слишком много данных по сети). Без GetQuery() и обычным OrderBy() трудно быть уверенным - но, как всегда, первое, что нужно сделать, это профилировать запрос с помощью трассировки. Посмотрите, какой запрос выполнен и какие данные возвращены. EFProf может сделать это легко, но, вероятно, достаточно SQL-трассировки.

+1

+1 - также существует малоизвестный 'ObjectQuery ' ext meth, называемый '.ToTraceString()' который показывает SQL-запрос, который должен быть выполнен. Очень удобно для ведения журнала. – RPM1984

+0

Извините, я пропустил это: 'GetQuery()' - один лайнер: 'return ObjectSet.AsQueryable();'. Я отредактирую и добавлю код для OrderBy() - я не знаю, откуда он был получен, возможно, здесь: linqextensions.codeplex.com/ –

+0

@JK - если честно, вам может быть достаточно просто проверить, код работает, если вы удалите '.AsQueryable()' –

Смежные вопросы