What you can do is use the Compose method, which can compose one expression with another:
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); }
Uses the following method to replace all instances of one expression with another:
public static Expression Replace(this Expression expression, Expression searchEx, Expression replaceEx) { return new ReplaceVisitor(searchEx, replaceEx).Visit(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); } }
Now you can write:
private static IQueryable<MyModel> FilterFirstName( IQueryable<MyModel> query, Expression<Func<MyModel, string>> selector, string searchText, string searchFilter) { switch (searchFilter.ToLower()) { case "contains": query = query.Where(selector.Compose( text => text.ToLower().Contains(searchText.ToLower()))); break; case "does not contain": query = query.Where(selector.Compose( text => !text.ToLower().Contains(searchText.ToLower()))); break; case "starts with": query = query.Where(selector.Compose( text => text.StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase))); break; case "ends with": query = query.Where(selector.Compose( text => text.EndsWith(searchText, StringComparison.InvariantCultureIgnoreCase))); break; case "equals": query = query.Where(selector.Compose( text => text.Equals(searchText, StringComparison.InvariantCultureIgnoreCase))); break; } return query; }
On the side of the note, you really should use enum to represent different types of filters for searchFilter , not for a string. This will make it much easier for the caller, as they will not need to enter the exact line without any good way to find out what the exact parameters are, or if the provided parameter is valid.