构建表达式根据数组的顺序进行排序

时间:2018-06-11 22:01:03

标签: c# linq expression

我正在尝试构建一个表达式,以根据数组中传递的值的顺序进行排序。我有大部分的概念,但我很困惑我如何选择表达式将三元组合在一起。

/// <summary>
/// This method will build an expression to map the order 
/// of the ownerkeys to it's index to use for sorting
/// I.E I have an array [DivisionOwnerGuid, CompanyOwnerGuid]
/// ownerKey == {DivisionOwnerGuid} ? 0
/// ownerKey == {CompanyOwnerGuid} ? 1 : Int32.MaxValue
/// </summary>
protected Expression<Func<IConfigurationValue, int>> GetOrderByExpression(
    guid[] ownerKeyByPriority)
{

    // x =>
    var parameter = Expression.Parameter(typeof(IConfigurationValue));
    ownerKeyByPriority.Select((ownerKey, index) => new {
        // Hack: Build the expressions using the same parameter
        // to avoid having to use Expression Visitor
        Condition = this.GetCondition(parameter, ownerKey),
        Priority = index
    }).Aggregate((accumulated, nextPriorityExpression) => {
        // How can I aggregate the conditions together
        Expression.Condition(nextPriorityExpression, nextPriorityExpression.Priority,  )
    });
}

protected BinaryExpression GetCondition(ParameterExpression parameter, guid ownerKey)
{
    // x => x.OwnerGlobalIdRowKey
    var property = Expression.Property(parameter, 
        nameof(IConfigurationValue.OwnerGlobalIdRowKey));

    // x => x.OwnerGlobalIdRowKey == { valueOf(ownerKey) }
    return Expression.Equal(property, Expression.Constant(ownerKey, 
        typeof(Guid)));
}

如何通过聚合函数继续传播三元链?

2 个答案:

答案 0 :(得分:2)

忽略GetCondition返回中的(潜在)类型错误,您可以通过正确的输入在Aggregate中累积嵌套三元运算符的主体,然后构建一个lambda:

Expression<Func<IConfigurationValue, int>> GetOrderByExpression(
    Guid[] ownerKeyByPriority) {

    // x =>
    var parameter = Expression.Parameter(typeof(IConfigurationValue));
    var body = ownerKeyByPriority.Select((ownerKey, index) => new {
        // Hack: Build the expressions using the same parameter
        // to avoid having to use Expression Visitor
        Test = this.GetCondition(parameter, ownerKey),
        Priority = Expression.Constant(index)
    }).Aggregate((Expression)Expression.Constant(int.MaxValue), (accumulated, nextPriorityExpression) =>
        Expression.Condition(nextPriorityExpression.Test, nextPriorityExpression.Priority, accumulated)
    );

    return (Expression<Func<IConfigurationValue, int>>)Expression.Lambda(body, parameter);
}

答案 1 :(得分:0)

我最终写了一个可与属性和简单表达式一起使用的扩展方法

public static Expression<Func<TEntity, int>> GetOrderByArrayPriorityExpression<TEntity, TValue>(this Expression<Func<TEntity, TValue>> selectorExpression, TValue[] orderedValues)
{       
    var body = orderedValues.Select((value, index) => new {
        //Note: Build the expressions using the same parameter to avoid having to use Expression Visitor
        //x => x.Property == {value}
        Condition = Expression.Equal(selectorExpression.Body, Expression.Constant(value, typeof(TValue))),
        Priority = Expression.Constant(index)
    }).Aggregate((Expression)Expression.Constant(int.MaxValue), (accumulated, nextPriorityExpression) =>

        //This generates the conditions (in reverse which is also fine)
        // x =>
        // x.Property == {GlobalOwnerGuid} ? 3 : 
        // x.Property == {ClientOwnerGuid} ? 2 :
        // x.Property == {CompanyOwnerGuid} ? 1 :
        // x.Property == {DivisionOwnerGuid} ? 0 : Int.MaxValue
        Expression.Condition(nextPriorityExpression.Condition, nextPriorityExpression.Priority, accumulated)
    );

    // reuse to original parameter to avoid using an expression visitor
    return (Expression<Func<TEntity, int>>)Expression.Lambda(body, selectorExpression.Parameters);
}