问题 LINQ multiple where子句


我有一个课程表,我需要根据搜索框中输入的关键字进行搜索。 这是一个示例查询:

SELECT * FROM Courses WHERE 
Title LIKE '%word%' OR Title LIKE '%excel%' OR 
Contents LIKE '%word%' OR Contents LIKE '%excel%'

如何在LINQ中转换它,LINQ将根据每个关键字动态生成WHERE语句。

我尝试使用PredicateBuilder只要字段是VARCHAR就可以正常工作。对于“TEXT”字段,不会生成引号,从而导致编译器给出错误消息。这是PredicateBuilder生成的SQL

SELECT [t0].[CoursesID], [t0].[Title], [t0].[Contents], [t0].[Active], 
FROM [dbo].[Courses] AS [t0]
WHERE ([t0].[Title] LIKE '%word%') OR ([t0].[Contents] LIKE %word%) OR 
([t0].Title] LIKE '%excel%') OR ([t0].[Contents] LIKE %excel%)

请注意,“内容”字段没有单引号,这是数据库中的文本字段。

有没有简单的方法来构建WHERE语句并将其与查询一起附加?有没有人知道如果没有PredicateBuilder我怎么能这样做?

提前致谢。


10544
2018-02-28 11:14


起源



答案:


既然你正在使用LINQ,我想你正在对抗LINQ-to-SQL数据上下文吗?我没有备用DataContext来测试这个,但这应该给你一些想法。

我不知道它是否会对数据上下文起作用,但大多数都是非常基本的东西(链接OR运算符和包含方法调用),所以当查询转换为SQL时它不应该导致问题。

首先,我创建一个自定义函数来构建我的谓词:

Func<string, Func<DataItem, bool>> buildKeywordPredicate =
    keyword =>
        x => x.Title.Contains(keyword)
            || x.Contents.Contains(keyword);

这是一个函数,它接受一个字符串关键字然后返回另一个函数,该函数接受一个DataItem并根据该关键字进行检查。

基本上,如果你传入“Stack”,你会得到一个谓词: x => x.Title.Contains("Stack") || x.Contents.Contains("Stack")

接下来,由于有许多可能的关键字,你需要用OR操作链接它,我创建另一个辅助函数来链接2个谓词和一个OR

Func<Func<DataItem,bool>, Func<DataItem, bool>, Func<DataItem, bool>> buildOrPredicate =
    (pred1, pred2) =>
        x => pred1(x) || pred2(x);

此函数接受2个谓词,然后使用OR运算将它们连接起来。

有了这两个函数,我可以构建我的where谓词,如下所示:

foreach (var word in keywords) {            
    filter = filter == null
        ? buildKeywordPredicate(word)
        : buildOrPredicate(filter, buildKeywordPredicate(word));
}

循环内的第一行基本上检查过滤器是否为空。如果是,那么我们想要为我们构建一个简单的关键字过滤器。

否则,如果过滤器不为null,我们需要使用OR操作链接现有过滤器,因此我们将现有过滤器和新的关键字过滤器传递给buildOrPredicate来执行此操作。

然后我们现在可以创建查询的WHERE部分:

var result = data.Where(filter);

传递我们刚刚构建的复杂谓词。

我不知道这是否与使用PredicateBuilder有所不同,但由于我们将查询转换推迟到LINQ-to-SQL引擎,因此应该没有任何问题。

但正如我所说,我没有针对真实的数据上下文测试它,所以如果有任何问题你可以写在评论中。

这是我为测试而构建的控制台应用程序: http://pastebin.com/feb8cc1e

希望这可以帮助!


编辑: 有关在LINQ中正确使用表达式树的更通用和可重用的版本,请查看Thomas Petricek的博客文章: http://tomasp.net/articles/dynamic-linq-queries.aspx


13
2018-02-28 11:56



这将不幸地仅用于功能。要使这与Surface Trees一起工作,你需要使用这样的技巧: tomasp.net/articles/dynamic-linq-queries.aspx - Tomas Petricek
这是你在那里做的一些壮举!..同样的技巧,但更通用,更令人敬畏......无论如何,我现在是你博客的订阅者:-) - chakrit
谢谢,这个工作。 tomasp.net/articles/dynamic-linq-queries.aspx  - 托马斯·彼得塞克 - Amir
我正在寻找一个简单的解决方案。类似于:var q = q.Where(<t-sql statement>)如何编写自定义Where语句并将其附加到LINQ? - Amir
返回IEnumerable,我正在寻找IQueryable。所以,我试图将它封装在一个Expression中,但得到了一个例外,即SQL无法弄清楚.Invoke是什么,这是有道理的,但我不知道下一步该做什么(除了放弃这个方法)。 - Gary


答案:


既然你正在使用LINQ,我想你正在对抗LINQ-to-SQL数据上下文吗?我没有备用DataContext来测试这个,但这应该给你一些想法。

我不知道它是否会对数据上下文起作用,但大多数都是非常基本的东西(链接OR运算符和包含方法调用),所以当查询转换为SQL时它不应该导致问题。

首先,我创建一个自定义函数来构建我的谓词:

Func<string, Func<DataItem, bool>> buildKeywordPredicate =
    keyword =>
        x => x.Title.Contains(keyword)
            || x.Contents.Contains(keyword);

这是一个函数,它接受一个字符串关键字然后返回另一个函数,该函数接受一个DataItem并根据该关键字进行检查。

基本上,如果你传入“Stack”,你会得到一个谓词: x => x.Title.Contains("Stack") || x.Contents.Contains("Stack")

接下来,由于有许多可能的关键字,你需要用OR操作链接它,我创建另一个辅助函数来链接2个谓词和一个OR

Func<Func<DataItem,bool>, Func<DataItem, bool>, Func<DataItem, bool>> buildOrPredicate =
    (pred1, pred2) =>
        x => pred1(x) || pred2(x);

此函数接受2个谓词,然后使用OR运算将它们连接起来。

有了这两个函数,我可以构建我的where谓词,如下所示:

foreach (var word in keywords) {            
    filter = filter == null
        ? buildKeywordPredicate(word)
        : buildOrPredicate(filter, buildKeywordPredicate(word));
}

循环内的第一行基本上检查过滤器是否为空。如果是,那么我们想要为我们构建一个简单的关键字过滤器。

否则,如果过滤器不为null,我们需要使用OR操作链接现有过滤器,因此我们将现有过滤器和新的关键字过滤器传递给buildOrPredicate来执行此操作。

然后我们现在可以创建查询的WHERE部分:

var result = data.Where(filter);

传递我们刚刚构建的复杂谓词。

我不知道这是否与使用PredicateBuilder有所不同,但由于我们将查询转换推迟到LINQ-to-SQL引擎,因此应该没有任何问题。

但正如我所说,我没有针对真实的数据上下文测试它,所以如果有任何问题你可以写在评论中。

这是我为测试而构建的控制台应用程序: http://pastebin.com/feb8cc1e

希望这可以帮助!


编辑: 有关在LINQ中正确使用表达式树的更通用和可重用的版本,请查看Thomas Petricek的博客文章: http://tomasp.net/articles/dynamic-linq-queries.aspx


13
2018-02-28 11:56



这将不幸地仅用于功能。要使这与Surface Trees一起工作,你需要使用这样的技巧: tomasp.net/articles/dynamic-linq-queries.aspx - Tomas Petricek
这是你在那里做的一些壮举!..同样的技巧,但更通用,更令人敬畏......无论如何,我现在是你博客的订阅者:-) - chakrit
谢谢,这个工作。 tomasp.net/articles/dynamic-linq-queries.aspx  - 托马斯·彼得塞克 - Amir
我正在寻找一个简单的解决方案。类似于:var q = q.Where(<t-sql statement>)如何编写自定义Where语句并将其附加到LINQ? - Amir
返回IEnumerable,我正在寻找IQueryable。所以,我试图将它封装在一个Expression中,但得到了一个例外,即SQL无法弄清楚.Invoke是什么,这是有道理的,但我不知道下一步该做什么(除了放弃这个方法)。 - Gary


由于谓词构建器不知道调用Contains方法的属性的DB类型,我想这可能是linq到sql中的一个问题。您是否尝试过使用普通查询(不使用谓词构建器)和包含Contains的TEXT列?


0
2018-02-28 11:55