使用lambda防爆pressions复杂的LINQ排序 [英] Complex LINQ sorting using Lambda Expressions

查看:166
本文介绍了使用lambda防爆pressions复杂的LINQ排序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有人有/知道的IQueryable.OrderBy扩展,它需要一个防爆pression(检索,例如,通过反射)的?我相信函数看起来是这样的:

 公共静态的IQueryable< TEntity>排序依据< TEntity>
    (这IQueryable的< TEntity>来源,例pression SORTEX pression)
 

防爆pression会被认为是一个防爆pression< Func键< TEntity,T>> ,其中TEntity是同一对象进行排序上,T是需要被以创建新的IQueryable确定的类型。

我发现扩展,需要一个字符串,包括动态的LINQ的例子很多,像这样的:

 公共静态的IQueryable< TEntity>排序依据< TEntity>(
    这IQueryable的< TEntity>源,串SORTEX pression)
 

如果有可能采取的字符串,并使用反射来查找来自有问题的对象的类型,它应该也有可能采取防爆pression,并得到这是正确的,在防爆值类型pression。


以下是为什么我喜欢这个,您可能会或可能不会需要一个详细的解释。

我有复杂的记录相当大的列表进行排序。因为名单是这么久,我preFER有在数据库方面做了排序。为了处理更复杂的特性,我创建了防爆pressions提供的排序功能,像这样:

 如果(model.sortEx pression ==PlannedValue)
{
    防爆pression< Func键< BDopp,小数>>分拣机= BDopp.PlannedValueSorter;
    如果(model.sortDirection ==降序)
        哎呀= opps.OrderByDescending(分拣机).AsQueryable();
    其他
        哎呀= opps.OrderBy(分拣机).AsQueryable();
}
 

BDOpp.PlannedValueSorter检索静态EX pression从允许排序不哎呀要做对象的类型的IQueryable的还是:

 公共静态防爆pression< Func键< BDopp,小数>> PlannedValueSorter
    {
        得到
        {
            返回Z => z.BudgetSchedules
                。凡(S => s.Type == 1)
                .SUM(S => s.Value * s.Workshare * z.valueFactor / 100/100);
        }
    }
 

排序为简单的属性可以通过与使用反射来构建基于作为字符串传递的属性名称的前pression扩展方法。

这工作得很好,但对于复杂的类型,我还需要分支逻辑,我宁愿不这样做。我宁愿做的是检查是否包含前pression静态属性,然后简单地应用它。我能得到的前pression是这样的:

 的PropertyInfo信息= typeof运算(BDopp).GetProperty(model.sortEx pression +分拣机,
    BindingFlags.Static | BindingFlags.Public);
防爆pression EXPR =(出pression)info.GetValue(NULL,NULL);
 

对于PlannedValue财产,这使我的前pression排序PlannedValueSorter,我已经知道了的作品。


更新:

各周围挖已经得到了我什么,我想可能是一些进展:

 公共静态的IQueryable< TEntity>排序依据< TEntity>(这IQueryable的< TEntity>源,
    防爆pression< Func键< TEntity,动态>> SORTEX pression)
    {
        VAR一元= SORTEX pression.Body为UnaryEx pression;
        键入actualEx pressionType = unary.Operand.Type;
 

actualEx pressionType实际上是防爆pression的返回类型(对于这个特殊的属性,它的十进制)。

不幸的是,我的试验和错误大多只是工作,因为我还没有我的包裹围绕如何这一切工作的大脑,所以我尝试更新查询,像这样是行不通的:

  MethodCallEx pression resultExp =前pression.Call(typeof运算(可查询)
            排序依据,
            新类型[] {typeof运算(TEntity)已,actualEx pressionType},
            source.Ex pression,SORTEX pression);
        返回source.Provider.CreateQuery< TEntity>(resultExp);
 

它编译好的,但下面的错误被抛出在运行时:

  

没有泛型方法排序依据的类型System.Linq.Queryable'是   与提供的类型参数和参数兼容。无类型   如果方法是非泛型参数应该被提供。

解决方案

好了,我已经有了一个解决办法:

 公共静态的IQueryable< TEntity>排序依据< TEntity>(
    这IQueryable的< TEntity>资源,
    防爆pression< Func键< TEntity,动态>> SORTEX pression,
    布尔递减)
    {
        VAR一元= SORTEX pression.Body为UnaryEx pression;
        VAR操作数= unary.Operand;
        键入actualEx pressionType = operand.Type;

        MethodCallEx pression resultExp =
            防爆pression.Call(typeof运算(可查询)
                降? OrderByDescending:排序依据,
                新类型[] {typeof运算(TEntity)已,actualEx pressionType},
                source.Ex pression,
                防爆pression.Lambda(操作数,SORTEX pression.Parameters));
        返回source.Provider.CreateQuery< TEntity>(resultExp);
    }
 

的布尔降是允许的标准排序依据和OrderByDescending重载。这里有两个重大突破,至少对我来说:

  1. 获取操作数超出了前pression。
  2. 在使用前pression.Call和Ex pression.Lambda创造新的EX pression - 这允许我使用实际的类型变量,而防爆pression< Func键< TEntity,T>> 语法要求使用多数民众赞成在编译时已知类型

Does anyone have/know of an IQueryable.OrderBy extension that takes an Expression (retrieved, for example, by Reflection)? I believe the function would look something like this:

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

Expression would be assumed to be an Expression<Func<TEntity, T>> where TEntity is the same object being sorted on, and T is a type that needs to be determined in order to create the new IQueryable.

I've found many examples of extensions that take a string, including Dynamic Linq, like this:

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

If it's possible to take the string and use Reflection to look up the type from the object in question, it should also be possible to take the Expression, and get the value type which is right there IN the Expression.


Following is a detailed explanation of why I'd like to have this, which you may or may not need.

I have a rather large list of complex records to sort. Because the list is so long, I prefer to have the sorting done on the database side. To handle more complex properties, I've created Expressions that provide the sorting functionality, like so:

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 retrieves a static expression from the object which allows sorting to be done without opps are still of type 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);
        }
    }

Sorting for simple properties is done with Extension methods that use Reflection to build an expression based on the name of the property passed as a string.

This works well, but for the complex types, I still need branching logic, and I'd rather not do that. What I'd rather do is check for a static property containing the expression, and then simply apply it. I can get the expression like this:

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

For the PlannedValue property, this gets me the expression sorted in PlannedValueSorter, which I already know works.


Update:

Various digging around has gotten me what I think might be some progress:

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 is in fact the return type of the Expression (for this particular property, it's decimal).

Unfortunately I'm mostly just working by trial and error, since I haven't yet wrapped my brain around how all this works, so my attempt to update the query like so is not working:

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

It compiles okay, but the following error is thrown at runtime:

No generic method 'OrderBy' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.

解决方案

Okay, I've got a solution:

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

The bool descending is to allow for the standard OrderBy and OrderByDescending overloads. Two major breakthroughs here, at least for me:

  1. Getting the Operand out of the expression.
  2. Using Expression.Call and Expression.Lambda to create the new expression - this allows me to use an actual "Type" variable, whereas the Expression<Func<TEntity, T>> syntax requires you to use a type that's known at compile time.

这篇关于使用lambda防爆pressions复杂的LINQ排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆