【问题标题】:Get IQueryable<T> where any field of T contains a given string获取 IQueryable<T> ,其中 T 的任何字段都包含给定的字符串
【发布时间】:2013-12-12 20:27:59
【问题描述】:

我正在使用 EF5 有一个由字符串、日期、可为空的日期和整数组成的 20 个大型属性实体。是否可以使用 Linq 对每个字段进行包含。

我不想为每个字段都构建一个 linq 语句

results = list.Where(house => house.Date1.ToString().Contains(search)||
                              house.Address1.Contains(search)||
                              house.Address2.Contains(search)||
                              house.Address3.Contains(search)||
                              .........................)

我想我想将所有属性转换为字符串(在必要时),然后对它们中的每一个进行包含?理想情况下,这将发生在数据库级别。

我想我不能将它们连接起来,因为如果两个字段的结尾和开头创建了搜索文本,这可能会导致错误匹配?有什么想法吗?

更新

我目前是 LINQ string[] against multiple fields 的第二个答案,使用 LinqKit AsExpandable 和 EF 转换函数到基于类型的字符串。但是我似乎无法找到将 DateTime 转换为字符串的 SqlFunction

【问题讨论】:

  • 在每个字段上使用相同的搜索字符串对我来说看起来真的很糟糕。
  • 我同意。但是,客户希望从单个文本框中搜索数据中的所有字段。不过,目前这是一个概念证明
  • @ScottChamberlain 这本质上是一个全文搜索。作为有意义的业务需求。但不确定 EF 是否是完成这项工作的最佳工具。
  • @Servy 我就是这个意思,就是不知道怎么说。
  • 看来您应该创建一个存储 Id + Body 的新表,其中 body 是您需要搜索的字段的串联,然后在 Body 上添加全文索引

标签: c# linq entity-framework entity-framework-5


【解决方案1】:

所以这里的总体思路是使用PredicateBuilder 对一组表达式进行或运算,每个表达式代表一个Contains 对给定属性的检查。您可以使用反射来查找给定类型的所有属性以创建表达式。

public static Expression<Func<T, bool>> SearchAllFields<T>(string searchText)
{
    var t = Expression.Parameter(typeof(T));
    Expression body = Expression.Constant(false);

    var containsMethod = typeof(string).GetMethod("Contains"
        , new[] { typeof(string) });
    var toStringMethod = typeof(object).GetMethod("ToString");

    var stringProperties = typeof(T).GetProperties()
        .Where(property => property.PropertyType == typeof(string));

    foreach (var property in stringProperties)
    {
        var stringValue = Expression.Call(Expression.Property(t, property.Name),
            toStringMethod);
        var nextExpression = Expression.Call(stringValue,
            containsMethod,
            Expression.Constant(searchText));

        body = Expression.OrElse(body, nextExpression);
    }

    return Expression.Lambda<Func<T, bool>>(body, t);
}

【讨论】:

  • 好的,所以这对数据库不起作用,因为它使用 ToString 等?我无法让它对数据库起作用,因为我无法将 DateTime 转换为 EF 级别的字符串。我想我会用这个或类似的东西做一个 IQUEeryable 扩展,只需要在服务器上做
  • @GraemeMiller 您可以更改它并使用 SqlFunctions.StringConvert 代替数字字段(如果不是,则转换为双精度或十进制)。因此,代码可以使用字符串类型的字段(无需使用 StringConvert)、“数字”字段(使用 SqlFunctions.StringConvert),但不能使用布尔或复杂类型(无论如何不要认为它有意义).. .
【解决方案2】:

我最近创建了一个 nuget 包,它使用扩展方法解决了这个问题

https://www.nuget.org/packages/NinjaNye.SearchExtensions/

这将允许以下代码...

using NinjaNye.SearchExtensions;
//...
var result = list.Search(h => search, h.Date1.ToString(),
                                      h.Address1,
                                      h.Address2,
                                      h.Address3,
                                      ...etc...)

如果您想使用代码,请查看 github 页面: https://github.com/ninjanye/SearchExtensions

当连接到 sql 数据库时,你会得到类似于以下 sql 的内容:

SELECT [Extent1].[Date1] AS [Date1], 
       [Extent1].[Address1] AS [Address1], 
       [Extent1].[Address2] AS [Address2], 
       [Extent1].[Address3] AS [Address3] 
FROM   [dbo].[Table] AS [Extent1]
WHERE ([Extent1].[Address1] LIKE N'%searchTerm%') 
   OR ([Extent1].[Address2] LIKE N'%searchTerm%') 
   OR ([Extent1].[Address3] LIKE N'%searchTerm%')

希望对你有帮助

【讨论】:

    猜你喜欢
    • 2020-12-22
    • 2014-09-10
    • 2011-01-08
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多