如何在拆分后使用 entity framework 搜索文本?
How to search a text using entity framework after split?
我有一个实体模型
public class User{
public string FirstName {get;set;}
public string LastName {get;set;}
public string Department {get;set;}
}
所以我想使用 entity framework 核心 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();
...........
...........
...........
}
但是不行。
那我该怎么做呢?
我认为你不需要 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();
虽然不太准确,但return“john john”也是这样,但这种情况很少见。
下面的怎么样
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.x 在大多数情况下并不真正支持 All
和 Any
的翻译,并且您的代码略有错误,我认为您真正想要的是:
var queryable = _context.Users.Where(u => terms.All(m => u.Department.Contains(m) ||
u.FirstName.Contains(m) ||
u.LastName.Contains(m)));
由于无法翻译,您需要将其重新格式化为可以翻译的代码。
使用 LINQKit,您可以使用 PredicateBuilder
创建一个扩展,将查询重新映射到每个术语的一系列 &&
测试中:
// 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"))
);
另一种解决方案是汇总您的搜索词:
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)
)
);
我有一个实体模型
public class User{
public string FirstName {get;set;}
public string LastName {get;set;}
public string Department {get;set;}
}
所以我想使用 entity framework 核心 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();
...........
...........
...........
}
但是不行。
那我该怎么做呢?
我认为你不需要 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();
虽然不太准确,但return“john john”也是这样,但这种情况很少见。
下面的怎么样
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.x 在大多数情况下并不真正支持 All
和 Any
的翻译,并且您的代码略有错误,我认为您真正想要的是:
var queryable = _context.Users.Where(u => terms.All(m => u.Department.Contains(m) ||
u.FirstName.Contains(m) ||
u.LastName.Contains(m)));
由于无法翻译,您需要将其重新格式化为可以翻译的代码。
使用 LINQKit,您可以使用 PredicateBuilder
创建一个扩展,将查询重新映射到每个术语的一系列 &&
测试中:
// 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"))
);
另一种解决方案是汇总您的搜索词:
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)
)
);