3

クエリをページ分割する拡張メソッドを構築しようとしています。ただし、例外を回避するために: 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 メソッドを呼び出す必要があることを要求することで、ソリューションを簡素化しました。これにより、ツリー全体を検索するのではなく、式ツリーの現在のノードのみをチェックできました。だからこれは私がやったことです:OrderByOrderByDescending

// 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);
    }
4

2 に答える 2

2

ここで説明するように式自体を確認するか、ここで説明するようにコンパイル時の型を確認できますOrderBy動的linqも式に(またはOrderByDescending)を追加するため、前者はうまくいくはずです。

于 2012-06-05T20:39:34.710 に答える
1

どういうわけか、意味のないことを解決しようとしているように見えます。

メソッドを使用するときにランタイム例外を取得したくない場合。良い!ただし、このような場合は、動的Linqを使用しないでください。これは、実行時例外が発生IOrderedQueryableし、Paginateメソッドで使用されるためです。

DynamicLinqを使用したい。良い!ただし、そのような場合は、実行時にコードをテストするいくつかのテスト(つまり統合テスト)が必要であり、Paginateメソッドも同じ方法でテストする必要があります。

実行時の式ツリーの探索は、実行時の例外と同じです。順序付けされていないデータでPaginateメソッドを使用することについてプログラマーに通知しませんでした。

于 2012-06-05T20:43:52.160 に答える