构建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);
         }
     }
}

有人知道如何用表达式实现这一点吗?

构建LINQ表达式以转换为SQL输出中的圆括号

您应该在和语句之前创建或语句:

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