12

Expression<Func<T, TProperty>>要素の順序付けに使用されるコレクションを格納し、オブジェクトに対して格納されたリストを実行する方法を探していIQueryable<T>ます (基になるプロバイダーは Entity Framework です)。

たとえば、次のようなことをしたいと思います (これは疑似コードです)。

public class Program
{
    public static void Main(string[] args)
    {
        OrderClause<User> orderBys = new OrderClause<User>();
        orderBys.AddOrderBy(u => u.Firstname);
        orderBys.AddOrderBy(u => u.Lastname);
        orderBys.AddOrderBy(u => u.Age);

        Repository<User> userRepository = new Repository<User>();
        IEnumerable<User> result = userRepository.Query(orderBys.OrderByClauses);
    }
}

order by 句 (順序付けするプロパティ):

public class OrderClause<T>
{
    public void AddOrderBy<TProperty>(Expression<Func<T, TProperty>> orderBySelector)
    {
        _list.Add(orderBySelector);
    }

    public IEnumerable<Expression<Func<T, ???>>> OrderByClauses
    {
        get { return _list; }
    }
}

私のクエリメソッドを含むリポジトリ:

public class Repository<T>
{
    public IEnumerable<T> Query(IEnumerable<OrderClause<T>> clauses)
    {
        foreach (OrderClause<T, ???> clause in clauses)
        {
            _query = _query.OrderBy(clause);
        }

        return _query.ToList();
    }
}

私の最初のアイデアはExpression<Func<T, TProperty>>、 を文字列 (並べ替えるプロパティ名) に変換することでした。したがって、基本的には、型付きリスト (TProperty が定数ではないため、これは不可能です) を格納する代わりに、並べ替えるプロパティを含む文字列のリストを格納します。

しかし、バックを再構築できないため、これは機能しません(IQueryable.OrderBy がas パラメーターExpressionを取るため必要です)。Expression<Func<T, TKey>>

また、(Expression.Convert を使用して) Expression を動的に作成しようとしましたExpression<Func<T, object>>が、エンティティ フレームワークから、Expression.Convert ステートメントを処理できないという例外が発生しました。

可能であれば、 Dynamic Linq Libraryのような外部ライブラリを使用したくありません。

4

4 に答える 4

11

dynamicこれは/ リフレクション ソリューションが適切な数少ないケースの 1 つです。

私はあなたがこのようなものが欲しいと思いますか?(行間を読み、必要に応じて構造を変更しました)。

public class OrderClauseList<T>
{
    private readonly List<LambdaExpression> _list = new List<LambdaExpression>();

    public void AddOrderBy<TProperty>(Expression<Func<T, TProperty>> orderBySelector)
    {
        _list.Add(orderBySelector);
    }

    public IEnumerable<LambdaExpression> OrderByClauses
    {
        get { return _list; }
    }
}

public class Repository<T>
{
    private IQueryable<T> _source = ... // Don't know how this works

    public IEnumerable<T> Query(OrderClause<T> clauseList)
    {
        // Needs validation, e.g. null-reference or empty clause-list. 

        var clauses = clauseList.OrderByClauses;

        IOrderedQueryable<T> result = Queryable.OrderBy(_source, 
                                                        (dynamic)clauses.First());

        foreach (var clause in clauses.Skip(1))
        {
            result = Queryable.ThenBy(result, (dynamic)clause);
        }

        return result.ToList();
    }
}

重要なトリックは、C#dynamicに恐ろしいオーバーロード解決と型推論を行わせることです。さらに、 を使用しているにもかかわらず、上記dynamicは実際には型安全であると私は信じています!

于 2013-05-22T13:52:55.210 に答える
0

EF Core では、次のヘルパー クラスを使用できます

public static class OrderBuilder
{
    public static IQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> queryable, params Tuple<Expression<Func<TSource, object>>, bool>[] keySelectors)
    {
        if (keySelectors == null || keySelectors.Length == 0) return queryable;

        return keySelectors.Aggregate(queryable, (current, keySelector) => keySelector.Item2 ? current.OrderDescending(keySelector.Item1) : current.Order(keySelector.Item1));
    }

    private static bool IsOrdered<TSource>(this IQueryable<TSource> queryable)
    {
        if (queryable == null) throw new ArgumentNullException(nameof(queryable));

        return queryable.Expression.Type == typeof(IOrderedQueryable<TSource>);
    }

    private static IQueryable<TSource> Order<TSource, TKey>(this IQueryable<TSource> queryable, Expression<Func<TSource, TKey>> keySelector)
    {
        if (!queryable.IsOrdered()) return queryable.OrderBy(keySelector);

        var orderedQuery = queryable as IOrderedQueryable<TSource>;

        return (orderedQuery ?? throw new InvalidOperationException()).ThenBy(keySelector);
    }

    private static IQueryable<TSource> OrderDescending<TSource, TKey>(this IQueryable<TSource> queryable, Expression<Func<TSource, TKey>> keySelector)
    {
        if (!queryable.IsOrdered()) return queryable.OrderByDescending(keySelector);

        var orderedQuery = queryable as IOrderedQueryable<TSource>;
        return (orderedQuery ?? throw new InvalidOperationException()).ThenByDescending(keySelector);
    }
}

そして、それを次のように使用します..以下の例では、次のメンバーを持つ Player という名前のクラスを使用しています..(簡潔にするためにコードを省略しています)

 public class Player
 {
    ...

    public string FirstName { get; set; 
    public int GenderTypeId { get; set; } 
    public int PlayingExperience { get; set; }

好きなように並べ替え、性別による並べ替え、プレイ経験の降順 (タプルの真の値に注意してください)、および名前を組み合わせることができます。

    var combinedOrder = new[]
    {
        new Tuple<Expression<Func<Player, object>>, bool>(p => p.GenderTypeId, false),
        new Tuple<Expression<Func<Player, object>>, bool>(p => p.PlayingExperience, true),
        new Tuple<Expression<Func<Player, object>>, bool>(p => p.FirstName, false),
    };

次のように注文するだけです

        var data = context.Set<Player>()
            .OrderBy(combinedOrder)
            .ToArray();
于 2019-04-01T22:19:21.403 に答える