クエリをページ分割する拡張メソッドを構築しようとしています。ただし、例外を回避するために: System.NotSupportedException はユーザー コードによって処理されませんでした Message=The method 'Skip' is only supported for sorted input in LINQ to Entities. メソッド「OrderBy」は、メソッド「Skip」の前に呼び出す必要があります。
OrderBy が適用されているかどうかを確認したいのですが、そうでない場合はクエリを返します...次のようなもの:
public static IQueryable<T> Paginate<T>(this IQueryable<T> query, int page, int pageSize = 50, int total = -1)
{
// check if OrderBy was applied
// THIS DOES NOT WORK!!!
//try
//{
// var orderedQueryable = query as IOrderedQueryable<T>;
//}
//catch (Exception)
//{
// // if the cast throws OrderBy was not applied <-- DOES 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);
}
より興味深いものにするために、Mycrosoft のDynamic Expression API (別名 Dynamic LINQ) を使用しているため、呼び出しコードは次のようになります。
return query
.OrderBy("Customer.LastName DESC, Customer.FirstName")
.Paginate(1,25, 2345)
.ToArray();
または、このように厳密に型指定された式を使用して呼び出すことができます
return query
.OrderByDescending(c=>c.LastName)
.ThenBy(c=>c.FirstName)
.Paginate(1,25,2345)
.ToArray();
このようなチェックは可能ですか?メソッド署名での使用にうんざりしましたが、OrderBy(string expression) を使用するとIOrderableQueryable<T>
Dynamic Linq が返さIOrderableQueryable
れないため、拡張機能は適用されません...
アップデート
提案(@GertArnoldによって指摘された)を使用する唯一の実行可能な解決策は、式ツリーを調べることでした。ただし、全体を使用する代わりに、またはExpressionVisitor
の直後に Paginate メソッドを呼び出す必要があることを要求することで、ソリューションを簡素化しました。これにより、ツリー全体を検索するのではなく、式ツリーの現在のノードのみをチェックできました。だからこれは私がやったことです:OrderBy
OrderByDescending
// 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 that either OrderBy or OrderByDescending was applied just before calling Paginate....
if (query.Expression.NodeType != ExpressionType.Call)
{
//TODO: logging -> "You have to apply OrderBy() or OrderByDescending() just 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 to apply OrderBy() or OrderByDescending() just 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);
}