C# – Extending IQueryable to return objects where a property contains a string

cc#-3.0iqueryablelinq-to-sql

I see a lot of code similar to the following

var customrs = MyDataContext.Customers.Where(...);
if (!String.IsNullOrEmpty(input)) {
  customers = customers.Where(c => c.Email.Contains(input));
}

I would like to put this in an extension method that checks the input is valid before invoking Where on IQueryable so that it can be called like

customers = MyDataContext.Customers.Where(...)
  .ContainsText(c => c.Email, input);

My extension method looks like this

public static IQueryable<T> ContainsText<T>(this IQueryable<T> source, Expression<Func<T, string>> selector, string text) {
  if (String.IsNullOrEmpty(text) {
    return source;
  }
  else {
    //do something here
  }
}

How can I call Contains() on the expression parsed? Or is there another way to return IQueryable of results where the expression parsed contains the text parsed?

Update: It's for Linq to Sql

Best Answer

tvanfosson had the right idea with building the expression which led to this answer to this question. So for completeness here is a full working solution

The expression builder

public static class ExpressionBuilder {

  public static Expression<Func<T, bool>> ContainsText<T>(string propertyName, string text) {
    var paramExp = Expression.Parameter(typeof(T), "type");
    var propExp = Expression.Property(paramExp, propertyName);
    var methodInfo = typeof(string).GetMethod("Contains", new[] { typeof(string) });
    var valueExp = Expression.Constant(text, typeof(string));
    var methCall = Expression.Call(propExp, methodInfo, valueExp);
    return Expression.Lambda<Func<T, bool>>(methCall, paramExp);
  }

}

The extension method

public static class IQueryableExtensions {

  public static IQueryable<T> ContainsText<T>(this IQueryable<T> source, Expression<Func<T, string>> selector, string text) {
    if (source == null) {
      throw new ArgumentNullException();
    }
    if (text.IsNullOrEmpty()) {
      return source;
    }
    string propName = ((MemberExpression)selector.Body).Member.Name;
    return source.Where(ExpressionBuilder.ContainsText<T>(propName, text));
  }

}

Invoked like

var customers = MyDataContext.Customers.Where(/* some code */)
  .ContainsText(c => c.Email, input);