, , , Contains. .
PredicateBuilder, , OR. PredicateBuilder, :
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
}
}
/, Expression :
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
, , Compose. , , , .
public static Expression<Func<TFirstParam, TResult>>
Compose<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
, . , , . , , Compose, , Contains , ORs .
public static IQueryable<T> AnyFieldContains<T>(
this IQueryable<T> query,
string searchText,
params Expression<Func<T, string>>[] fieldSelectors)
{
var filter = PredicateBuilder.False<T>();
foreach (var selector in fieldSelectors)
{
filter = filter.Or(selector.Compose(
value => value.Contains(searchText)));
}
return query.Where(filter);
}
, , , . , :
IQueryable<Foo> query = db.Foo;
string searchText = "wil tom";
var searchExpressions = searchText.Split(' ');
foreach (var expression in searchExpressions)
{
query = query.AnyFieldContains(expression,
foo => foo.FirstName,
foo => foo.LastName,
foo => foo.Phone);
}
var result = query.Any();
, ( , , , , , , - , , ), , , , . , , , "":
public static IQueryable<T> AnyFieldContains<T>(
this IQueryable<T> query,
string searchText)
{
return AnyFieldContains(query, searchText,
typeof(T).GetProperties()
.Select(prop => CreateSelector<T>(prop))
.ToArray());
}
private static Expression<Func<T, string>> CreateSelector<T>(PropertyInfo prop)
{
var param = Expression.Parameter(typeof(T));
Expression body = Expression.Property(param, prop);
if (prop.PropertyType == typeof(decimal?))
body = Expression.Call(body, typeof(SqlFunctions)
.GetMethod("StringConvert", new[] { typeof(decimal?) }));
else if (prop.PropertyType == typeof(double?))
body = Expression.Call(body, typeof(SqlFunctions)
.GetMethod("StringConvert", new[] { typeof(double?) }));
return Expression.Lambda<Func<T, string>>(body, param);
}