0

式を受け取るIQueryable.OrderBy拡張機能を持っている/知っている人はいますか(たとえば、Reflectionによって取得されます)?関数は次のようになると思います。

public static IQueryable<TEntity> OrderBy<TEntity>
    (this IQueryable<TEntity> source, Expression sortExpression)

式はExpression<Func<TEntity, T>>、TEntityがソートされているのと同じオブジェクトであり、Tが新しいIQueryableを作成するために決定する必要があるタイプであると想定されます。

次のようなDynamicLinqを含む、文字列を受け取る拡張機能の例をたくさん見つけました。

public static IQueryable<TEntity> OrderBy<TEntity>(
    this IQueryable<TEntity> source, string sortExpression) 

文字列を取得し、Reflectionを使用して問題のオブジェクトから型を検索できる場合は、式を取得して、式のすぐそこにある値型を取得することもできるはずです。


以下は、私がこれを欲しがっている理由の詳細な説明です。あなたがこれを必要とするかもしれないし、必要としないかもしれません。

並べ替える複雑なレコードのリストがかなりたくさんあります。リストが非常に長いので、データベース側でソートを行うことを好みます。より複雑なプロパティを処理するために、次のような並べ替え機能を提供する式を作成しました。

if (model.sortExpression == "PlannedValue")
{
    Expression<Func<BDopp, decimal>> sorter = BDopp.PlannedValueSorter;         
    if (model.sortDirection == "DESC")
        opps = opps.OrderByDescending(sorter).AsQueryable();
    else
        opps = opps.OrderBy(sorter).AsQueryable();
}

BDOpp.PlannedValueSorterは、オブジェクトから静的式を取得します。これにより、oppsなしで並べ替えを実行できます。タイプはIQueryableです。

public static Expression<Func<BDopp, decimal>> PlannedValueSorter
    {
        get
        {
            return z => z.BudgetSchedules
                .Where(s => s.Type == 1)
                .Sum(s => s.Value * s.Workshare * z.valueFactor / 100 / 100);
        }
    }

単純なプロパティの並べ替えは、文字列として渡されたプロパティの名前に基づいて式を作成するためにReflectionを使用するExtensionメソッドを使用して行われます。

これはうまく機能しますが、複雑なタイプの場合でも、分岐ロジックが必要であり、そうはしません。私がやりたいのは、式を含む静的プロパティをチェックしてから、それを適用することです。私はこのような表現を得ることができます:

PropertyInfo info = typeof(BDopp).GetProperty(model.sortExpression + "Sorter",
    BindingFlags.Static | BindingFlags.Public);
Expression expr = (Expression)info.GetValue(null, null);

PlannedValueプロパティの場合、これにより、PlannedValueSorterで並べ替えられた式が取得されます。これは、すでに機能していることがわかっています。


アップデート:

さまざまな掘り下げによって、私はある程度の進歩があると思います。

public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, 
    Expression<Func<TEntity, dynamic>> sortExpression)
    {
        var unary = sortExpression.Body as UnaryExpression;
        Type actualExpressionType = unary.Operand.Type;

actualExpressionTypeは、実際には式の戻り型です(この特定のプロパティの場合、10進数です)。

残念ながら、私はほとんど試行錯誤を繰り返しています。これがどのように機能するかについてまだ頭を悩ませていないため、このようにクエリを更新しようとしても機能しません。

        MethodCallExpression resultExp = Expression.Call(typeof(Queryable), 
            "OrderBy",
            new Type[] { typeof(TEntity), actualExpressionType },
            source.Expression, sortExpression);
        return source.Provider.CreateQuery<TEntity>(resultExp);

コンパイルは正常ですが、実行時に次のエラーがスローされます。

タイプ'System.Linq.Queryable'のジェネリックメソッド'OrderBy'は、提供されたタイプ引数および引数と互換性がありません。メソッドがジェネリックでない場合は、型引数を指定しないでください。

4

3 に答える 3

1

私が理解しているように、あなたは持っていて、それでExpression注文/フィルタリングしたいと考えています。驚くかもしれませんが、その単純さには次のようなものがあります。

Expression<Func<TEntity, T>> sortExpr = ...; //you already have this
var ordered = myQuery.OrderBy(sortExpr);

OrderByは常にを使用するExpressionため、直接使用できます。type の変数 ( typeExpressionのオブジェクトを指すExpression<Func<TEntity, T>>) しかなく、それが何であるかを静的に知らないT場合は、次のようにすることができます。

Expression sortExpr = ...; //you already have this
var ordered = myQuery.OrderBy((dynamic)sortExpr);

dynamicリフレクションを使用して実行時にこれを把握します。自分で反省する必要はありません。ここで必要なのは、オーバーロードの解決 (C# ランタイム バインダーによって実行される) だけです。

于 2013-03-19T22:36:41.857 に答える
1

さて、解決策があります:

public static IQueryable<TEntity> OrderBy<TEntity>(
    this IQueryable<TEntity> source, 
    Expression<Func<TEntity, dynamic>> sortExpression, 
    bool descending)
    {
        var unary = sortExpression.Body as UnaryExpression;
        var operand = unary.Operand;
        Type actualExpressionType = operand.Type;

        MethodCallExpression resultExp = 
            Expression.Call(typeof(Queryable), 
                descending? "OrderByDescending" : "OrderBy",
                new Type[] { typeof(TEntity), actualExpressionType },
                source.Expression, 
                Expression.Lambda(operand, sortExpression.Parameters));
        return source.Provider.CreateQuery<TEntity>(resultExp);
    }

bool の降順は、標準の OrderBy および OrderByDescending のオーバーロードを許可することです。少なくとも私にとっては、ここで 2 つの大きなブレークスルーがあります。

  1. 式からオペランドを取得します。
  2. Expression.Call と Expression.Lambda を使用して新しい式を作成します。これにより、実際の「Type」変数を使用できますが、Expression<Func<TEntity, T>>構文ではコンパイル時に既知の型を使用する必要があります。
于 2013-03-20T20:18:46.570 に答える
0

これを試して:

public static IEnumerable<T> OrderBy<T>(
       this IEnumerable<T> collection, 
       string columnName
       //, SortDirection direction
)
{
    ParameterExpression param = Expression.Parameter(typeof(T), "x");   // x
    Expression property = Expression.Property(param, columnName);       // x.ColumnName
    Func<T, object> lambda = Expression.Lambda<Func<T, object>>(        // x => x.ColumnName
            Expression.Convert(property, typeof(object)),
            param)
        .Compile();

    Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> expression = (c, f) => c.OrderBy(f); // here you can use OrderByDescending basing on SortDirection

    IEnumerable<T> sorted = expression(collection, lambda);
    return sorted;
}

使用法:

IEnumerable<T> collection = ...
IEnumerable<T> ordered = collection.OrderBy("PropName");

コード プロジェクトサンドボックスを参照してください。

于 2013-03-19T22:34:54.253 に答える