构建LINQ表达式以转换为SQL输出中的圆括号
本文关键字:输出 圆括号 SQL LINQ 表达式 转换 构建 | 更新日期: 2023-09-27 18:12:21
我使用实体框架6和Waseem Sabjee的解决方案在运行时追加表达式,这工作得很好。
然而,我无法解决的是如何将表达式的部分与SQL中的括号分开。
例如:SELECT * FROM SOMETABLE WHERE Field1 = 'somevalue' AND (FIELD2 = 1 OR FIELD2 = 2 OR FIELD2 = 3)
不是SELECT * FROM SOMETABLE WHERE Field1 = 'somevalue' AND FIELD2 = 1 OR FIELD2 = 2 OR FIELD2 = 3
将输出不同的结果,并且在第二次会出错这是我真正想要的。
(尽管在SQL中我会使用"in"子句来执行"OR"部分)
通常情况下,我只是把括号周围的过滤器的相关部分,但在我的情况下,在运行时,我不知道我需要多少OR语句,因为我传递的值的列表,其中的数量只有在运行时才知道,即:
public Search(IList<int> vals, string filter){
Expression<Func<Event, bool>> filter = x => x.Field1 == filter;
bool first = true;
for(int i in vals){
if (first){
filter = filter.And(x -> x.Field2 == i);
first = false;
}
else{
filter = filter.Or(x -> x.Field2 == i);
}
}
}
有人知道如何用表达式实现这一点吗?
您应该在和语句之前创建或语句:
public Search(IList<int> vals, string filter){
Expression<Func<Event, bool>> andFilter = x => x.Field1 == filter;
var firstVal = vals.First();
Expression<Func<Event, bool>> orFilter = x=>x.Field2 == firstVal;
foreach(int i in vals.Skip(1)){
orFilter = orFilter.Or(x -> x.Field2 == i);
}
andFilter = andFilter.And(orFilter);
}
好的,所以我不得不思考一点横向解决这个问题,因为我找不到一种方法来做到这一点与表达式构建器(可能仍然有一种方法!)
相反,我转向Roslyn,我在这里找到了参考。
使用Rosyln,你基本上可以将字符串编译成c#代码,并返回输出以在运行时进一步使用。
因此,我现在不再附加表达式,而是将表达式创建为字符串中的一段代码,使用Rosyln编译它,并返回用于过滤数据的新表达式。
它是这样工作的:
ScriptEngine scriptEngine = new ScriptEngine();
string exp = "public System.Linq.Expressions.Expression<System.Func<my.namespace.object,bool>> CreateFilter(){System.Linq.Expressions.Expression<System.Func<my.namespace.object,bool>> filter = e => e.Field1.Contains('"a'");return filter;}CreateFilter();";
第一部分将新方法设置为字符串。
Roslyn.Scripting.Session s = scriptEngine.CreateSession();
s.AddReference(typeof(System.Linq.Expressions.Expression).Assembly);
s.AddReference(typeof(my.namespace.object).Assembly);
您需要添加对代码所需的任何非系统程序集的引用,就像任何其他项目一样。
Expression<Func<Event, bool>> filter = (Expression<Func<Event, bool>>)s.Execute(exp);
最后,只需执行返回新表达式的代码。
使用这种技术,您可以轻松地在任何您喜欢的地方添加任何括号,并使用循环和其他条件语句构建表达式。
我在NuGet上找到了Roslyn
很抱歉回复晚了(这边电源有问题)。这是我的解决方案(仅限代码)
// at this point query will be: SELECT * FROM SOMTABLE WHERE Field1 = 'Cat'
Expression<Func<Event, bool>> filterExpression = x => x.Field1 == filter;
if (vals.Count > 0)
{
// at this point query will be: SELECT * FROM SOMTABLE WHERE Field1 = 'Cat' AND Field2 = 1
filterExpression = filterExpression.And(x => x.Field2 == vals.First());
}
if (vals.Count > 1)
{
// at this point query will be: SELECT * FROM SOMTABLE WHERE Field1 = 'Cat' AND (Field2 = 1 OR Field2 = 3 OR Field2 = 4)
filterExpression = filterExpression.Or(x => vals.Skip(1).Contains(x.Field2));
}
我在这里发布了一个vs解决方案的链接:http://www.waseem-sabjee.com/code/LinqExpressionFilterIssue.zip