EF Core - 无法查询列表 <string> 中是否存在 entity.Name
EF Core - Unable to query if entity.Name exists in List<string>
使用 Entity Framework 核心 3.1.7
我在包含产品的数据库中有一个 table。
public class Product
{
public int Id {get; set;}
public string Name {get; set;}
}
然后我希望用户能够使用搜索字段在 UI table 中查找某些产品。
当查询出现时,我尝试执行以下操作:
var searchParameters = query.SearchParameters.ToLower().Split(' ', ',', '+').Distinct();
var result = _context.Products
.Where(p => searchParameters.Any()
&& (searchParameters.Any(x => p.Name.ToLower().Contains(x)) //Version 1
).ToList();
或替代方案
searchParameters.Any(x => EF.Functions.Like(p.Name, "%" + x + "%")) //Version 2
但是不管我如何调整这个看似简单的东西,我得到了:
The LINQ expression 'DbSet
.Where(p => __searchParameters_0
.Any(x => p.Name.ToLower().Contains(x)))' could not be translated. Either rewrite the query in a form that can be translated,
or switch to client evaluation explicitly by inserting a call to
either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync()
我意识到 .ToLower()
会成为一个问题,所以我想 运行 LIKE 语句用于不区分大小写的搜索,就像 SQL 查询一样。但即便如此,List<string>
仍未被翻译。
如果您愿意使用LINQKit(或模拟谓词构建器部分),您可以使用扩展方法将Any(
...Contains)
表达式扩展为“或”表达式:
public static class LinqKitExt { // using LINQKit
// keyFne - extract string key from row
// searchTerms - IEnumerable<string> where one must be contained by a row's key
// dbq.Where(r => searchTerms.Any(s => keyFne(r).Contains(s)))
public static IQueryable<T> WhereContainsAny<T>(this IQueryable<T> dbq, Expression<Func<T,string>> keyFne, IEnumerable<string> searchTerms) {
var pred = PredicateBuilder.New<T>();
foreach (var s in searchTerms)
pred = pred.Or(r => keyFne.Invoke(r).Contains(s));
return dbq.Where((Expression<Func<T,bool>>)pred.Expand());
}
}
(以及 Where/OrderBy[降序] Any/All Contains/StartsWith 的 51 个其他变体。)
那你就可以这样使用了
var result = _context.Products
.WhereContainsAny(r => r.Name, searchParameters)
.ToList();
PS 寻求另一个答案,我意识到将测试拉到调用者可以消除大部分变化:
// 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.Any(s => testFne(r,s)))
public static IQueryable<T> WhereAny<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.Or(r => testFne.Invoke(r, s));
return dbq.Where((Expression<Func<T,bool>>)pred.Expand());
}
那么您只需拨打:
var result = _context.Products
.WhereAny(searchParameters, (r,s) => r.Name.Contains(s))
.ToList();
使用 Entity Framework 核心 3.1.7
我在包含产品的数据库中有一个 table。
public class Product
{
public int Id {get; set;}
public string Name {get; set;}
}
然后我希望用户能够使用搜索字段在 UI table 中查找某些产品。 当查询出现时,我尝试执行以下操作:
var searchParameters = query.SearchParameters.ToLower().Split(' ', ',', '+').Distinct();
var result = _context.Products
.Where(p => searchParameters.Any()
&& (searchParameters.Any(x => p.Name.ToLower().Contains(x)) //Version 1
).ToList();
或替代方案
searchParameters.Any(x => EF.Functions.Like(p.Name, "%" + x + "%")) //Version 2
但是不管我如何调整这个看似简单的东西,我得到了:
The LINQ expression 'DbSet .Where(p => __searchParameters_0 .Any(x => p.Name.ToLower().Contains(x)))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync()
我意识到 .ToLower()
会成为一个问题,所以我想 运行 LIKE 语句用于不区分大小写的搜索,就像 SQL 查询一样。但即便如此,List<string>
仍未被翻译。
如果您愿意使用LINQKit(或模拟谓词构建器部分),您可以使用扩展方法将Any(
...Contains)
表达式扩展为“或”表达式:
public static class LinqKitExt { // using LINQKit
// keyFne - extract string key from row
// searchTerms - IEnumerable<string> where one must be contained by a row's key
// dbq.Where(r => searchTerms.Any(s => keyFne(r).Contains(s)))
public static IQueryable<T> WhereContainsAny<T>(this IQueryable<T> dbq, Expression<Func<T,string>> keyFne, IEnumerable<string> searchTerms) {
var pred = PredicateBuilder.New<T>();
foreach (var s in searchTerms)
pred = pred.Or(r => keyFne.Invoke(r).Contains(s));
return dbq.Where((Expression<Func<T,bool>>)pred.Expand());
}
}
(以及 Where/OrderBy[降序] Any/All Contains/StartsWith 的 51 个其他变体。)
那你就可以这样使用了
var result = _context.Products
.WhereContainsAny(r => r.Name, searchParameters)
.ToList();
PS 寻求另一个答案,我意识到将测试拉到调用者可以消除大部分变化:
// 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.Any(s => testFne(r,s)))
public static IQueryable<T> WhereAny<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.Or(r => testFne.Invoke(r, s));
return dbq.Where((Expression<Func<T,bool>>)pred.Expand());
}
那么您只需拨打:
var result = _context.Products
.WhereAny(searchParameters, (r,s) => r.Name.Contains(s))
.ToList();