-1

我有一个表单,最终导致从具有string Qty,等string SelectedQtyOperator的组合框和数据库列中获得>, 。我试图找到某种方法来在 LINQ 表达式中对此进行评估,而无需为每个选项编写 ifelse/switch 语句。==<=?intQty

目前我有:

data = data.Where(m => Convert.ToInt32(m.Qty) [somehow evaluate SlQtyOp] Convert.ToInt32(Qty)).Select(m => m);

是否可以在表达式中执行此操作,或者有某种类型的辅助函数来评估它并返回正确的表达式?

编辑:

我忘了提到data被初始化为:

var data = db.MyDatabase.Select(m => m);

其中 db 是 DataContext

对于那些使用 LINQ-to-Object 并阅读本文的人

当我尝试在LINQ-to-SQL中执行此操作时(由于我自己的疏忽,我最初没有提到),Jim MischelSriram Sakthivel发布了可能对您有所帮助的不同解决方案。

4

5 回答 5

4

我会做这样的事情:

bool IsLessThan(int a, int b)
{
    return a < b;
}

bool IsGreaterThan(int a, int b)
{
    return a > b;
}

以同样的方式创建IsEqualIsLessEqual和。IsGreaterEqualIsNotEqual

并创建一个委托:

Func<int, int, bool> comparisonFunc;

在调用 LINQ 之前,分配委托:

switch (oper)
{
    case "<" : comparisonFunc = IsLessThan; break;
    case ">" : comparisonFunc = IsGreaterThan; break;
    // etc.
}

然后您的 LINQ 表达式变为:

data = data.Where(m => comparisonFunc(m.Qty, Convert.ToInt32(Qty))).Select(m => m);

现在,如果您的某些字段是int,有些是double,等等,那么您的方法会略有不同:

bool IsLessThan(int rslt)
{
    return rslt < 0;
}

bool IsGreaterEqual(int rslt)
{
    return rslt >= 0;
}

等等。您的代表略有不同:

Func<int, bool> comparisonFunc;

IComparer然后在 LINQ 表达式中使用for 类型:

data = data.Where(m => comparisonFunc(m.Qty.CompareTo(Convert.ToInt32(Qty)))).Select(m => m);

或者,如果它是double

data = data.Where(m => comparisonFunc(m.Qty.CompareTo(Convert.ToDouble(Qty)))).Select(m => m);

顺便说一句,在这种情况下,不需要Select. 您没有进行投影(即您按原样获取对象),因此您可以消除Select.

于 2013-10-29T19:29:33.123 回答
1

处理不需要使用其他库的任务的一种简单方法是分段构建查询:首先查询表并添加Where您可能拥有的其他子句。之后,构造一个switch像这样的简单语句:

IQueryable<MyDataType> data = ... // Put the initial query/table here
var qty = Convert.ToInt32(Qty);
switch (SelectedQtyOperator) {
    case ">": data = data.Where(m.Qty > qty ); break;
    case "<": data = data.Where(m.Qty < qty ); break;
    case "==": data = data.Where(m.Qty == qty ); break;
    case ">=": data = data.Where(m.Qty >= qty ); break;
    case "<=": data = data.Where(m.Qty <= qty ); break;
    default: /* Throw an exception: this shouldn't happen */ break;
}
foreach (var d in data) {
    ...
}
于 2013-10-29T19:27:47.887 回答
1

没有if-elseor switch,你不能用 LINQ 做到这一点,你将需要Dynamic LINQ

于 2013-10-29T19:24:12.767 回答
0

我认为可以通过反射来做一些事情,但这样的事情也可以:

switch (SlQtyOp)
{
case ">":
    data = data.Where(m => m.Qty > Convert.ToInt32(Qty)).Select(m => m);
case "<":
    data = data.Where(m => m.Qty < Convert.ToInt32(Qty)).Select(m => m);
case "=":
    data = data.Where(m => m.Qty = Convert.ToInt32(Qty)).Select(m => m);
}

不过,使用开关可能是最简单的。

于 2013-10-29T19:29:46.133 回答
0

因此,鉴于这是针对 anIQueryable而不是 an IEnumerable,您将需要操作表达式,而不是函数。首先,我们将定义一个字典,其中包含每个运算符的字符串版本,并将其映射到表示该操作的表达式:

var mappings = new Dictionary<string, Expression<Func<int, int, bool>>>()
{
    {">", ( Expression<Func<int, int, bool>>)((a,b)=> a > b)},
    {"<", ( Expression<Func<int, int, bool>>)((a,b)=> a < b)},
    {"==", ( Expression<Func<int, int, bool>>)((a,b)=> a == b)},
};

可以添加其他操作。

还有一个辅助方法,我们将使用它在整个某个表达式中用另一个表达式替换一个表达式的所有实例。

它使用此访问者:

public class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

并且只是用更简洁的语法包装它:

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}

从那里我们进入问题的核心,创建一个过滤给定查询的操作,其中使用给定运算符评估两个给定操作数。它需要过滤查询,每个操作数的选择器,然后是操作。然后它将操作参数的所有实例替换为操作数选择器,并将右侧选择器的参数替换为左侧选择器的参数,这样最终产品中就有一个实际参数,然后将其包装成一个 lambda 并传递至Where

public static IQueryable<TIn> WhereOperator<TIn, TLeft, TRight>(
    this IQueryable<TIn> query,
    Expression<Func<TIn, TLeft>> leftSelector,
    Expression<Func<TIn, TRight>> rightSelector,
    Expression<Func<TLeft, TRight, bool>> operation)
{
    var newRightBody = rightSelector.Body.Replace(rightSelector.Parameters[0],
        leftSelector.Parameters[0]);
    var newOperator = operation.Body.Replace(operation.Parameters[0], leftSelector.Body)
        .Replace(operation.Parameters[1], newRightBody);
    var lambda = Expression.Lambda<Func<TIn, bool>>(newOperator,
        leftSelector.Parameters[0]);
    return query.Where(lambda);
}

示例用法:

IQueryable<Tuple<int, int>> query = new[] { Tuple.Create(1, 2) }
    .AsQueryable();

var query2 = query.WhereOperator(pair => pair.Item1, pair => pair.Item2
    , mappings[">"]);

在这种情况下,我们使用内存中的可查询对象,但正如query2.Expression将向您展示的那样,这实际上看起来与调用相同:

var query3 = query.Where(pair => pair.Item1 > pair.Item2);
于 2013-10-29T21:30:37.223 回答