【问题标题】:How to build dynamic query with expression tree for PLINQ如何使用 PLINQ 的表达式树构建动态查询
【发布时间】:2014-04-08 05:14:59
【问题描述】:

我想为 PLINQ 制作定制的 OrderBy,但我不知道该怎么做。

对于 IQueryable,使用可以使用以下代码:

public static class QueryableExtensions
{
    public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string sortProperty, ListSortDirection sortOrder)
    {
        var type = typeof(T);
        var property = type.GetProperty(sortProperty);
        var parameter = Expression.Parameter(type, "p");
        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
        var orderByExp = Expression.Lambda(propertyAccess, parameter);
        var typeArguments = new Type[] { type, property.PropertyType };
        var methodName = sortOrder == ListSortDirection.Ascending ? "OrderBy" : "OrderByDescending";
        var resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, Expression.Quote(orderByExp));

        return source.Provider.CreateQuery<T>(resultExp);
    }
}

但是对于 ParallelQuery,没有这样的属性 Provider 和 Expresss。 有人知道怎么做吗?

public static class QueryableExtensions
{
    public static ParallelQuery<T> OrderBy<T>(this ParallelQuery<T> source, string sortProperty, ListSortDirection sortOrder)
    {
        ...
    }
}

【问题讨论】:

    标签: c# expression-trees plinq


    【解决方案1】:

    有了IQueryable,你就不需要Provider了,创建表达式然后直接调用OrderBy/OrderByDescending就够了。唯一的问题是OrderBy() 在排序属性的类型中是通用的,你不知道(不是静态的)。

    您可以通过使用反射调用OrderBy() 来解决这个问题。或者你可以使用dynamic:

    public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string sortProperty, ListSortDirection sortOrder)
    {
        var type = typeof(T);
        var property = type.GetProperty(sortProperty);
        var parameter = Expression.Parameter(type, "p");
        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
        var orderByExp = Expression.Lambda(propertyAccess, parameter);
    
        if (sortOrder == ListSortDirection.Ascending)
        {
            return Queryable.OrderBy(source, (dynamic)orderByExp);
        }
        else
        {
            return Queryable.OrderByDescending(source, (dynamic)orderByExp);
        }
    }
    

    您可以使用与 PLINQ 完全相同的方法:

    public static ParallelQuery<T> OrderBy<T>(this ParallelQuery<T> source, string sortProperty, ListSortDirection sortOrder)
    {
        var type = typeof(T);
        var property = type.GetProperty(sortProperty);
        var parameter = Expression.Parameter(type, "p");
        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
        var orderByFunc = Expression.Lambda(propertyAccess, parameter).Compile();
    
        if (sortOrder == ListSortDirection.Ascending)
        {
            return ParallelEnumerable.OrderBy(source, (dynamic)orderByFunc);
        }
        else
        {
            return ParallelEnumerable.OrderByDescending(source, (dynamic)orderByFunc);
        }
    }
    

    【讨论】:

    • 还有一个问题,如果我有 sortProperty 返回类型对象,但始终存储具有整数类型的值,如何避免装箱/拆箱?
    • @luciferlu 如果属性的类型是object,那么无论如何该值已经被装箱了。而且我认为这段代码不会拆箱。
    • 在我提到的情况下,不知何故,我们可以添加以下代码来提高性能: propertyAccess = Expression.Convert(propertyAccess, [target type])
    【解决方案2】:

    只需在并行查询上调用 AsQueryable,然后在完成后调用 AsParallel 以获取并行查询:

    public static ParallelQuery<T> OrderBy<T>(this ParallelQuery<T> source, 
        string sortProperty, ListSortDirection sortOrder)
    {
        return source.AsQueryable()
            .OrderBy(sortProperty, sortOrder)
            .AsParallel();
    }
    

    【讨论】:

    • 但这实际上会并行执行OrderBy()吗?
    • 谢谢,但实际上这行不通。它会抛出 StackOverflow。
    猜你喜欢
    • 1970-01-01
    • 2018-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多