评估操作员符号并执行操作员

时间:2013-10-29 19:22:32

标签: c# linq linq-to-sql

我的表单最终导致来自具有string Qtystring SelectedQtyOperator>等的组合框中的==<=和数据库?intQty。我试图在LINQ表达式中找到一些方法来评估它,而无需为每个选项编写ifelse / switch语句。

目前我有:

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 Mischel &amp; Sriram Sakthivel 发布了可能对您有所帮助的不同解决方案。

5 个答案:

答案 0 :(得分:4)

我会做这样的事情:

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

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

以同样的方式创建IsEqualIsLessEqualIsGreaterEqualIsNotEqual

创建一个委托:

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;

您在LINQ表达式中使用IComparer作为类型:

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

答案 1 :(得分:1)

如果没有if-elseswitch,您无法使用LINQ执行此操作,则需要Dyamic LINQ

答案 2 :(得分: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) {
    ...
}

答案 3 :(得分: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);
}

尽管使用开关可能最容易。

答案 4 :(得分:0)

因此,鉴于这是针对IQueryable而不是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);