C # Entity Framework + Linq - как ускорить медленный запрос? - PullRequest
6 голосов
/ 20 ноября 2010

Это вопрос, связанный с C # MVC2 Jqgrid - как правильно выполнять пейджинг на стороне сервера? , где я спросил и нашел, как повысить производительность запроса к таблице с 2000 или около того строк. Производительность была улучшена с 10 до 1 секунды.

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

Вот несколько возможных идей:

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

Это действие 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;
}

Предложение .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);
}

1 Ответ

6 голосов
/ 20 ноября 2010

Меня беспокоит:

.Take(rows).AsQueryable()

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...