我的表单最终导致来自具有string Qty
,string SelectedQtyOperator
,>
等的组合框中的==
,<=
和数据库?int
列Qty
。我试图在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 发布了可能对您有所帮助的不同解决方案。
答案 0 :(得分:4)
我会做这样的事情:
bool IsLessThan(int a, int b)
{
return a < b;
}
bool IsGreaterThan(int a, int b)
{
return a > b;
}
以同样的方式创建IsEqual
,IsLessEqual
,IsGreaterEqual
和IsNotEqual
。
创建一个委托:
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-else
或switch
,您无法使用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);