entity framework - How to check if IQueryable<T> has OrderBy applied before before attempting Skip() and Take() -
i trying build extension method paginated query. in order avoid exception: system.notsupportedexception unhandled user code message=the method 'skip' supported sorted input in linq entities. method 'orderby' must called before method 'skip'.
i'd check if orderby applied , if not return query... this:
public static iqueryable<t> paginate<t>(this iqueryable<t> query, int page, int pagesize = 50, int total = -1) { // check if orderby applied // not work!!! //try //{ // var orderedqueryable = query iorderedqueryable<t>; //} //catch (exception) //{ // // if cast throws orderby not applied <-- not work!!! // return query; //} page = (page < 1) ? 1 : page; var limit = (pagesize <= 0 || (total >= 0 && pagesize > total)) ? 50 : pagesize; var skip = (page - 1)*limit; return query.skip(skip).take(limit); } to make things more interesting, using mycrosoft's dynamic expression api (aka dynamic linq) calling code looks like
return query .orderby("customer.lastname desc, customer.firstname") .paginate(1,25, 2345) .toarray(); or can invoke using typed expressions this
return query .orderbydescending(c=>c.lastname) .thenby(c=>c.firstname) .paginate(1,25,2345) .toarray(); is type of checking possible? tired using iorderablequeryable<t> in method signature dynamic linq not return iorderablequeryable when use orderby(string expression) extension not apply...
update
using suggestion (pointed out @gertarnold) workable solution inspect expression tree. however, instead of making use of entire expressionvisitor simplified solution requiring paginate method must called after orderby or orderbydescending. allowed me check current node in expression tree instead of searching whole tree. did:
// snip....class level private static readonly string[] paginationprerequisitemehods = new[] { "orderby", "orderbydescending" }; // snip paginate method public static iqueryable<t> paginate<t>(this iqueryable<t> query, int page, int pagesize = 50, int total = -1) { // require either orderby or orderbydescending applied before calling paginate.... if (query.expression.nodetype != expressiontype.call) { //todo: logging -> "you have apply orderby() or orderbydescending() before calling paginate()" return query; } var methodname = ((methodcallexpression) query.expression).method.name; if (!array.exists(paginationprerequisitemehods, s => s.equals(methodname, stringcomparison.invariantculture))) { //todo: logging -> "you have apply orderby() or orderbydescending() before calling paginate()" return query; } page = (page < 1) ? 1 : page; var limit = (pagesize <= 0 || (total >= 0 && pagesize > total)) ? 50 : pagesize; var skip = (page - 1)*limit; return query.skip(skip).take(limit); }
you can check expression itself, described here, or check compile-time type, described here. think former should work you, because dynamic linq adds orderby (or orderbydescending) expression.
Comments
Post a Comment