【问题标题】:How to search a text using entity framework after split?拆分后如何使用实体框架搜索文本?
【发布时间】:2020-09-01 14:30:09
【问题描述】:

我有一个实体模型

public class User{
   public string FirstName {get;set;}
   public string LastName {get;set;}
   public string Department {get;set;}
}

所以我想使用实体框架核心 3.1 在数据库中搜索类似 "john smith" 的文本。

我之前是拆分文字的。

    public async Task<IEnumerable<UserListViewModel>> Search(string search)
    {
        var terms = search.Split(" ");

        var queryable = _context.Users.Where(s => terms.All(m => s.Department.ToLower().Contains(m)) ||
                                                  terms.All(m => s.FirstName.ToLower().Contains(m)) ||
                                                  terms.All(m => s.LastName.ToLower().Contains(m))).AsQueryable();

        ...........
        ...........
        ...........
    }

但它不起作用。

那我该怎么做呢?

【问题讨论】:

  • 反之亦然,例如 s =&gt; terms.Contains(s.Department.ToLower()) 等。
  • @RomanRyzhiy 我认为这不会有帮助 - 想象一下搜索字符串是“j smith tech”,而您正试图在“信息技术”部门找到“John Smith”。

标签: c# entity-framework linq entity-framework-core entity-framework-core-3.1


【解决方案1】:

EF Core 3.x 在大多数情况下并不真正支持AllAny 的翻译,而且您的代码略有错误,我认为您真正想要的是:

var queryable = _context.Users.Where(u => terms.All(m => u.Department.Contains(m) ||
                                                         u.FirstName.Contains(m) ||
                                                         u.LastName.Contains(m)));

由于无法翻译,您需要将其重新格式化为可以翻译的代码。

使用LINQKit,您可以使用PredicateBuilder 创建一个扩展,将查询重新映射为每个术语的一系列&amp;&amp; 测试:

// searchTerms - IEnumerable<TKey> where all must be in a row's key
// testFne(row,searchTerm) - test one of searchTerms against a row
// dbq.Where(r => searchTerms.All(s => testFne(r,s)))
public static IQueryable<T> WhereAll<T,TKey>(this IQueryable<T> dbq, IEnumerable<TKey> searchTerms, Expression<Func<T, TKey, bool>> testFne) {
    var pred = PredicateBuilder.New<T>();
    foreach (var s in searchTerms)
        pred = pred.And(r => testFne.Invoke(r, s));

    return dbq.Where((Expression<Func<T,bool>>)pred.Expand());
}

你会像这样使用:

var queryable = _context.Users
                    .WhereAll(terms,
                              (u,m) => u.Department.Contains(m) ||
                                       u.FirstName.Contains(m) ||
                                       u.LastName.Contains(m));

对于“john smith”,扩展方法将创建等价于:

var queryable = _context.Users
                    .Where(u => (u.Department.Contains("john") ||
                                 u.FirstName.Contains("john") ||
                                 u.LastName.Contains("john")) &&
                                (u.Department.Contains("smith") ||
                                 u.FirstName.Contains("smith") ||
                                 u.LastName.Contains("smith"))
                           );

【讨论】:

    【解决方案2】:

    下面的怎么样。

      void Main()
      {
        var users = new List<User>
        {
            new User { FirstName = "John", LastName = "Smith", Department = "Web" },
            new User { FirstName = "Aaliyah", LastName = "Lin", Department = "Warehouse" },
            new User { FirstName = "Cristian", LastName = "Stone", Department = "Cleaning" },
            new User { FirstName = "Kierra", LastName = "Davidson", Department = "Mobile" },
            new User { FirstName = "Lizbeth", LastName = "Gregory", Department = "Web" }
        };
        
        var search = "Lizbeth Gregory";
        var terms = search.ToLower().Split(' ');
        users.Where(s => terms.All(m => s.Department.ToLower().Contains(m)) ||
                                                          (terms.Any(m => s.FirstName.ToLower().Contains(m))) ||
                                                          terms.Any(m => s.LastName.ToLower().Contains(m)))
                                                          .Dump();
    }
    
    public class User
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Department { get; set; }
    }
    

    【讨论】:

    • 我认为问题是 EF Core 3.1 在大多数有用的情况下不会翻译 AllAny
    【解决方案3】:

    我认为你不需要terms.All。因为“john smith”是一个全名all,它不会出现在名字或姓氏字段中。

    我不确定以下是否可行。

    var queryable = _context.Users.Where(s => terms.Contains(m => s.Department.ToLower().Contains(m)) &&
                                                      terms.Contains(m => s.FirstName.ToLower().Contains(m)) ||
                                                      terms.All(m => s.LastName.ToLower().Contains(m))).AsQueryable();
    

    虽然不是很准确,但它也会以这种方式返回“john john”,但这种情况很少见。

    【讨论】:

      【解决方案4】:

      另一种解决方案是汇总您的搜索字词:

      var terms = search
          .Split(' ', StringSplitOptions.RemoveEmptyEntries);
      query = terms
          .Aggregate(query, (current, term) =>
               current.Where(x =>
                   x.FirstName.Contains(term)
                   || x.LastName.Contains(term)
                   || x.Department.Contains(term)
               )
          );
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-06-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多