首页 文章

表达式列表<Func <T,TProperty >>

提问于
浏览
10

我正在寻找一种方法来存储用于订购元素的 Expression<Func<T, TProperty>> 集合,然后针对 IQueryable<T> 对象执行存储列表(底层提供者是实体框架) .

例如,我想做这样的事情( this is 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);
    }
}

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不是常量,这是不可能的),我存储了一个字符串列表,其中包含要排序的属性 .

但这不起作用,因为我无法重建 Expression (我需要它,因为IQueryable.OrderBy将 Expression<Func<T, TKey>> 作为参数) .

我还尝试动态创建Expression(在Expression.Convert的帮助下),得到一个 Expression<Func<T, object>> 但是后来我从实体框架中得到一个异常,它说它无法处理Expression.Convert语句 .

如果可能的话,我不想使用像Dynamic Linq Library这样的外部库 .

3 回答

  • 2

    这是少数几种可能适合使用 dynamic /反射解决方案的情况之一 .

    我想你想要这样的东西? (我已经在线条之间阅读并对我认为必要的结构进行了一些更改) .

    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 ,但实际上是类型安全的!

  • 3

    一种方法是将所有sort子句“存储”在像 Func<IQueryable<T>, IOrderedQueryable<T>> 这样的函数中(即调用排序方法的函数):

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

    这样,您不必处理反射或 dynamic ,所有这些代码都是类型安全且相对容易理解的 .

  • 10

    您可以将lambda表达式作为 LambdaExpression 类型的实例存储在集合中 .

    或者更好的是,存储排序定义,除了表达式之外,每个排序定义都存储排序方向 .

    假设您有以下扩展方法

    public static IQueryable<T> OrderBy<T>(
        this IQueryable<T> source,
        SortDefinition sortDefinition) where T : class
    {
        MethodInfo method;
        Type sortKeyType = sortDefinition.Expression.ReturnType;
        if (sortDefinition.Direction == SortDirection.Ascending)
        {
            method = MethodHelper.OrderBy.MakeGenericMethod(
                typeof(T),
                sortKeyType);
        }
        else
        {
            method = MethodHelper.OrderByDescending.MakeGenericMethod(
                typeof(T),
                sortKeyType);
        }
    
        var result = (IQueryable<T>)method.Invoke(
            null,
            new object[] { source, sortDefinition.Expression });
        return result;
    }
    

    ThenBy 类似的方法 . 然后你可以做类似的事情

    myQueryable = myQueryable.OrderBy(sortDefinitions.First());
    
    myQueryable = sortDefinitions.Skip(1).Aggregate(
       myQueryable,
       (current, sortDefinition) => current.ThenBy(sortDefinition));
    

    以下是 SortDefinitionMethodHelper 的定义

    public class SortDefinition
    {
        public SortDirection Direction
        {
            get;
            set;
        }
    
        public LambdaExpression Expression
        {
            get;
            set;
        }
    }
    
    internal static class MethodHelper
    {
        static MethodHelper()
        {
            OrderBy = GetOrderByMethod();
            ThenBy = GetThenByMethod();
            OrderByDescending = GetOrderByDescendingMethod();
            ThenByDescending = GetThenByDescendingMethod();
        }
    
        public static MethodInfo OrderBy
        {
            get;
            private set;
        }
    
        public static MethodInfo ThenBy
        {
            get;
            private set;
        }
    
        public static MethodInfo OrderByDescending
        {
            get;
            private set;
        }
    
        public static MethodInfo ThenByDescending
        {
            get;
            private set;
        }
    
        private static MethodInfo GetOrderByMethod()
        {
            Expression<Func<IQueryable<object>, IOrderedQueryable<object>>> expr =
                q => q.OrderBy((Expression<Func<object, object>>)null);
    
            return ((MethodCallExpression)expr.Body).Method.GetGenericMethodDefinition();
        }
    
        private static MethodInfo GetThenByMethod()
        {
            Expression<Func<IOrderedQueryable<object>, IOrderedQueryable<object>>> expr =
                q => q.ThenBy((Expression<Func<object, object>>)null);
    
            return ((MethodCallExpression)expr.Body).Method.GetGenericMethodDefinition();
        }
    
        private static MethodInfo GetOrderByDescendingMethod()
        {
            Expression<Func<IQueryable<object>, IOrderedQueryable<object>>> expr =
                q => q.OrderByDescending((Expression<Func<object, object>>)null);
    
            return ((MethodCallExpression)expr.Body).Method.GetGenericMethodDefinition();
        }
    
        private static MethodInfo GetThenByDescendingMethod()
        {
            Expression<Func<IOrderedQueryable<object>, IOrderedQueryable<object>>> expr =
                q => q.ThenByDescending((Expression<Func<object, object>>)null);
    
            return ((MethodCallExpression)expr.Body).Method.GetGenericMethodDefinition();
        }
    }
    

相关问题