在多个列中搜索一个词
Search a term in multiple columns
我想创建一个扩展以在多列中搜索字词。
术语以 space 分隔,每个术语必须出现在至少一个给定的列中。
这是我到目前为止所做的:
public static IQueryable<TSource> SearchIn<TSource>(this IQueryable<TSource> query,
string searchText,
Expression<Func<TSource, string>> expression,
params Expression<Func<TSource, string>>[] expressions)
{
if (string.IsNullOrWhiteSpace(searchText))
{
return query;
}
// Concat expressions
expressions = new[] { expression }.Concat(expressions).ToArray();
// Format search text
var formattedSearchText = searchText.FormatForSearch();
var searchParts = formattedSearchText.Replace('\'', ' ').Split(' ');
// Initialize expression
var pe = Expression.Parameter(typeof(TSource), "entity");
var predicateBody = default(Expression);
// Search in each expressions, put OR in between
foreach (var expr in expressions)
{
var exprBody = default(Expression);
// Search for each words, put AND in between
foreach (var searchPart in searchParts)
{
// Create property or field expression
var left = Expression.PropertyOrField(pe, ((MemberExpression)expr.Body).Member.Name);
// Create the constant expression with current word
var search = Expression.Constant(searchPart, typeof(string));
// Create the contains function
var contains = Expression.Call(left, typeof(string).GetMethod(nameof(string.Contains), new Type[] { typeof(string) }), search);
// Check if there already a predicate body
if (exprBody == null)
{
exprBody = contains;
}
else
{
exprBody = Expression.And(exprBody, contains);
}
}
if (predicateBody == null)
{
predicateBody = exprBody;
}
else
{
predicateBody = Expression.OrElse(predicateBody, exprBody);
}
}
// Build the where method expression
var whereCallExpression = Expression.Call(
typeof(Queryable),
nameof(Queryable.Where),
new Type[] { query.ElementType },
query.Expression,
Expression.Lambda<Func<TSource, bool>>(predicateBody, new ParameterExpression[] { pe }));
// Apply the condition to the query and return it
return query.Provider.CreateQuery<TSource>(whereCallExpression);
}
只要给定的表达式很简单,它就可以很好地工作:
// It works well
query.SearchIn("foo", x => x.Column1, x => x.Column2);
但是在尝试通过导航属性进行导航时它不起作用:
// Not working
query.SearchIn("foo", x => x.Nav1.Column1);
它给了我一个例外。
'Column1' is not a member of type 'Nav1'.
我明白这个问题,但我找不到通过Nav1
的解决方案。
我需要这方面的帮助。
不用解析 lambda 表达式主体,只需使用给定参数调用它:
var left = Expression.Invoke(expr, pe);
但是它只适用于 EF Core。
在 EF6 中,您需要获取 属性 或每个嵌套成员的字段,如下所示:
var left = expr.Body.ToString()
.Split('.')
.Skip(1) //skip the original parameter name
.Aggregate((Expression)pe, (a, c) => Expression.PropertyOrField(a, c));
它仅适用于简单的 lambda,例如:
x => x.Prop1.Nav1
如果这还不够,您需要一些更高级的解析算法,例如 ExpressionVisitor
。
我想创建一个扩展以在多列中搜索字词。 术语以 space 分隔,每个术语必须出现在至少一个给定的列中。
这是我到目前为止所做的:
public static IQueryable<TSource> SearchIn<TSource>(this IQueryable<TSource> query,
string searchText,
Expression<Func<TSource, string>> expression,
params Expression<Func<TSource, string>>[] expressions)
{
if (string.IsNullOrWhiteSpace(searchText))
{
return query;
}
// Concat expressions
expressions = new[] { expression }.Concat(expressions).ToArray();
// Format search text
var formattedSearchText = searchText.FormatForSearch();
var searchParts = formattedSearchText.Replace('\'', ' ').Split(' ');
// Initialize expression
var pe = Expression.Parameter(typeof(TSource), "entity");
var predicateBody = default(Expression);
// Search in each expressions, put OR in between
foreach (var expr in expressions)
{
var exprBody = default(Expression);
// Search for each words, put AND in between
foreach (var searchPart in searchParts)
{
// Create property or field expression
var left = Expression.PropertyOrField(pe, ((MemberExpression)expr.Body).Member.Name);
// Create the constant expression with current word
var search = Expression.Constant(searchPart, typeof(string));
// Create the contains function
var contains = Expression.Call(left, typeof(string).GetMethod(nameof(string.Contains), new Type[] { typeof(string) }), search);
// Check if there already a predicate body
if (exprBody == null)
{
exprBody = contains;
}
else
{
exprBody = Expression.And(exprBody, contains);
}
}
if (predicateBody == null)
{
predicateBody = exprBody;
}
else
{
predicateBody = Expression.OrElse(predicateBody, exprBody);
}
}
// Build the where method expression
var whereCallExpression = Expression.Call(
typeof(Queryable),
nameof(Queryable.Where),
new Type[] { query.ElementType },
query.Expression,
Expression.Lambda<Func<TSource, bool>>(predicateBody, new ParameterExpression[] { pe }));
// Apply the condition to the query and return it
return query.Provider.CreateQuery<TSource>(whereCallExpression);
}
只要给定的表达式很简单,它就可以很好地工作:
// It works well
query.SearchIn("foo", x => x.Column1, x => x.Column2);
但是在尝试通过导航属性进行导航时它不起作用:
// Not working
query.SearchIn("foo", x => x.Nav1.Column1);
它给了我一个例外。
'Column1' is not a member of type 'Nav1'.
我明白这个问题,但我找不到通过Nav1
的解决方案。
我需要这方面的帮助。
不用解析 lambda 表达式主体,只需使用给定参数调用它:
var left = Expression.Invoke(expr, pe);
但是它只适用于 EF Core。
在 EF6 中,您需要获取 属性 或每个嵌套成员的字段,如下所示:
var left = expr.Body.ToString()
.Split('.')
.Skip(1) //skip the original parameter name
.Aggregate((Expression)pe, (a, c) => Expression.PropertyOrField(a, c));
它仅适用于简单的 lambda,例如:
x => x.Prop1.Nav1
如果这还不够,您需要一些更高级的解析算法,例如 ExpressionVisitor
。