Question

Je cherche un moyen de stocker une collection d' Expression<Func<T, TProperty>> utilisées pour commander des éléments, puis pour exécuter la liste stockée sur un objet IQueryable<T> (le fournisseur sous-jacent est Entity Framework).

Par exemple, j'aimerais faire quelque chose comme ceci ( c'est du pseudo-code ):

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

Une clause order by (propriété sur laquelle commander):

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; }
    }
}

Un référentiel avec ma méthode de requête:

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();
    }
}

Ma première idée a été de convertir l' Expression<Func<T, TProperty>> en chaîne (le nom de la propriété sur laquelle trier). Donc, fondamentalement, au lieu de stocker une liste typée (ce qui n’est pas possible car la propriété TProperty n’est pas constante), je stocke une liste de chaînes avec les propriétés sur lesquelles trier.

Mais cela ne fonctionne pas car je ne peux pas reconstituer l' Expression (j'en ai besoin, car IQueryable.OrderBy prend une Expression<Func<T, TKey>> en paramètre).

J'ai également essayé de créer dynamiquement Expression (avec l'aide de Expression.Convert), afin de disposer d'un Expression<Func<T, object>> mais j'ai obtenu une exception dans le cadre de l'entité qui indiquait qu'il n'était pas capable de gérer l'Expression. Déclaration de conversion.

Si possible, je ne souhaite pas utiliser une bibliothèque externe telle que la bibliothèque dynamique Linq .

Réponse acceptée

C'est l'un des rares cas où une solution dynamic / réflexion peut être appropriée.

Je pense que tu veux quelque chose comme ça? (J'ai lu entre les lignes et apporté quelques modifications à votre structure lorsque j'ai jugé nécessaire).

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();
    }
}

L'essentiel est de faire en sorte que C # dynamic fasse la résolution de surcharge et l'inférence de types horribles pour nous. De plus, je pense que ce qui précède, malgré l'utilisation de dynamic , est en fait sûr pour le type!


Réponse populaire

Une façon de procéder consiste à «stocker» toutes les clauses de tri dans quelque chose comme Func<IQueryable<T>, IOrderedQueryable<T>> (c'est-à-dire une fonction qui appelle les méthodes de tri):

public class OrderClause<T>
{
    private Func<IQueryable<T>, IOrderedQueryable<T>> m_orderingFunction;

    public void AddOrderBy<TProperty>(Expression<Func<T, TProperty>> orderBySelector)
    {
        if (m_orderingFunction == null)
        {
            m_orderingFunction = q => q.OrderBy(orderBySelector);
        }
        else
        {
            // required so that m_orderingFunction doesn't reference itself
            var orderingFunction = m_orderingFunction;
            m_orderingFunction = q => orderingFunction(q).ThenBy(orderBySelector);
        }
    }

    public IQueryable<T> Order(IQueryable<T> source)
    {
        if (m_orderingFunction == null)
            return source;

        return m_orderingFunction(source);
    }
}

De cette façon, vous n’avez pas à traiter avec la réflexion ou la dynamic , tout ce code est dactylographié sûr et relativement facile à comprendre.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi