I have a search string that can be "sub1 sub2 sub3"
and I want to write a proper Expression<Func<T, bool>>
that can find "sub1"
, "sub2"
, "sub3"
and "sub1 sub2 sub3"
in the x.Name
In the other hand I want to modify x.Name.ToLower().Contains(productParams.Search)
for my purpose.
Now I can search the term "sub1 sub2 sub3"
.
However, I want to search for sub-strings as well.
my expectation for the search is: "sub1" || "sub2" || "sub3" || "sub1 sub2 sub3"
productParams.Search = "sub1 sub2 sub3"
How can do it?
public class ProductsSpecification : BaseSpecifcation<Product>
{
public ProductsSpecification(ProductSpecParams productParams) : base(x =>
(string.IsNullOrEmpty(productParams.Search) ||
x.Name.ToLower().Contains(productParams.Search)) &&
(!productParams.BrandId.HasValue || x.ProductBrandId == productParams.BrandId))
}
BaseSpecifcation:
public class BaseSpecifcation<T> : ISpecification<T>
{
public BaseSpecifcation(Expression<Func<T, bool>> criteria)
{
Criteria = criteria;
}
public Expression<Func<T, bool>> Criteria { get; }
}
CodePudding user response:
First of all, I created a helper class for generating predicates:
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 invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>> (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>> (Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
}
I created a private method in ProductsSpecification
and used my class helper:
private Expression<Func<Product, bool>> CreateProductFilter(ProductSpecParams productParams)
{
Expression<Func<Product, bool>> pr = PredicateBuilder.True<Product>(); // pr.Body.ToString() is "True"
if (!string.IsNullOrEmpty(productParams.Search) && !string.IsNullOrEmpty(productParams.Search.Trim()))
{
var searchValue = productParams.Search.Trim().ToLower();
pr = pr.And(a => a.Name.ToLower().Contains(searchValue));
foreach (var term in productParams.Search.ToLower().Split(' '))
{
string temp = term.Trim();
pr = pr.Or(a => a.Name.ToLower().Contains(temp));
}
}
if (productParams.BrandId.HasValue)
{
pr = pr.And(p => p.ProductBrandId == productParams.BrandId);
}
if (pr.Body.ToString() == "True")
{
return null;
}
return pr;
}
I modified the construcor of ProductsSpecification
:
public ProductsSpecification(ProductSpecParams productParams) : base()
{
Criteria = CreateProductFilter(productParams);
// rest of the code
}
Now, the filter works well!