The method 'OrderBy' must be called before the method 'Skip' Exception

nebula picture nebula · Jul 21, 2012 · Viewed 26.8k times · Source

I was trying to implement the jQgrid using MvcjQgrid and i got this exception.

System.NotSupportedException was unhandled by user code
  Message=The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'.

Though OrdeyBy is used before Skip method why it is generating the exception? How can it be solved?

I encountered the exception in the controller:

public ActionResult GridDataBasic(GridSettings gridSettings)
        {          
            var jobdescription = sm.GetJobDescription(gridSettings);
            var totalJobDescription = sm.CountJobDescription(gridSettings);

            var jsonData = new
            {
                total = totalJobDescription / gridSettings.PageSize + 1,
                page = gridSettings.PageIndex,
                records = totalJobDescription,
                rows = (
                    from j in jobdescription
                    select new
                    {
                        id = j.JobDescriptionID,
                        cell = new[] 
                    { 
                        j.JobDescriptionID.ToString(), 
                        j.JobTitle,
                        j.JobType.JobTypeName,
                        j.JobPriority.JobPriorityName,
                        j.JobType.Rate.ToString(),
                        j.CreationDate.ToShortDateString(),
                         j.JobDeadline.ToShortDateString(),

                    }
                    }).ToArray()
            };
            return Json(jsonData, JsonRequestBehavior.AllowGet);
        }

GetJobDescription Method and CountJobDescription Method

public int CountJobDescription(GridSettings gridSettings)
        {
            var jobdescription = _dataContext.JobDescriptions.AsQueryable();

            if (gridSettings.IsSearch)
            {
                jobdescription = gridSettings.Where.rules.Aggregate(jobdescription, FilterJobDescription);
            }
            return jobdescription.Count();
        }

        public IQueryable<JobDescription> GetJobDescription(GridSettings gridSettings)
        {
            var jobdescription = orderJobDescription(_dataContext.JobDescriptions.AsQueryable(), gridSettings.SortColumn, gridSettings.SortOrder);

            if (gridSettings.IsSearch)
            {
                jobdescription = gridSettings.Where.rules.Aggregate(jobdescription, FilterJobDescription);
            }

            return jobdescription.Skip((gridSettings.PageIndex - 1) * gridSettings.PageSize).Take(gridSettings.PageSize);
        }

And Finally FilterJobDescription and OrderJobDescription

private static IQueryable<JobDescription> FilterJobDescription(IQueryable<JobDescription> jobdescriptions, Rule rule)
        {
            if (rule.field == "JobDescriptionID")
            {
                int result;
                if (!int.TryParse(rule.data, out result))
                    return jobdescriptions;
                return jobdescriptions.Where(j => j.JobDescriptionID == Convert.ToInt32(rule.data));

            }

// Similar Statements

            return jobdescriptions;
        }



private IQueryable<JobDescription> orderJobDescription(IQueryable<JobDescription> jobdescriptions, string sortColumn, string sortOrder)
        {
            if (sortColumn == "JobDescriptionID")
                return (sortOrder == "desc") ? jobdescriptions.OrderByDescending(j => j.JobDescriptionID) : jobdescriptions.OrderBy(j => j.JobDescriptionID);

            return jobdescriptions;
        }

Answer

Slauma picture Slauma · Jul 21, 2012

The exception means that you always need a sorted input if you apply Skip, also in the case that the user doesn't click on a column to sort by. I could imagine that no sort column is specified when you open the grid view for the first time before the user can even click on a column header. To catch this case I would suggest to define some default sorting that you want when no other sorting criterion is given, for example:

switch (sortColumn)
{
    case "JobDescriptionID":
        return (sortOrder == "desc")
            ? jobdescriptions.OrderByDescending(j => j.JobDescriptionID)
            : jobdescriptions.OrderBy(j => j.JobDescriptionID);

    case "JobDescriptionTitle":
        return (sortOrder == "desc")
            ? jobdescriptions.OrderByDescending(j => j.JobDescriptionTitle)
            : jobdescriptions.OrderBy(j => j.JobDescriptionTitle);

    // etc.

    default:
        return jobdescriptions.OrderBy(j => j.JobDescriptionID);
}

Edit

About your follow-up problems according to your comment: You cannot use ToString() in a LINQ to Entities query. And the next problem would be that you cannot create a string array in a query. I would suggest to load the data from the DB with their native types and then convert afterwards to strings (and to the string array) in memory:

rows = (from j in jobdescription
        select new
        {
            JobDescriptionID = j.JobDescriptionID,
            JobTitle = j.JobTitle,
            JobTypeName = j.JobType.JobTypeName,
            JobPriorityName = j.JobPriority.JobPriorityName,
            Rate = j.JobType.Rate,
            CreationDate = j.CreationDate,
            JobDeadline = j.JobDeadline
        })
        .AsEnumerable() // DB query runs here, the rest is in memory
        .Select(a => new
        {
            id = a.JobDescriptionID,
            cell = new[] 
            { 
                a.JobDescriptionID.ToString(), 
                a.JobTitle,
                a.JobTypeName,
                a.JobPriorityName,
                a.Rate.ToString(),
                a.CreationDate.ToShortDateString(),
                a.JobDeadline.ToShortDateString()
            }
        })
        .ToArray()