I started using the code that Jon Koeter posted from the blog post in another answer that no longer appears to exist.
However, I found it didn't really work properly, especially when using an IEnumerable
. Namely, it was resolving the enumerable using ToArray
and using a regex to match, rather than the built in functions.
Since I only want to resolve my IEnumerable
once I have finished filtering, I made some changes to convert to an IQueryable
then use the rest of the code to find the correct Entity Framework method and call that. This way, the query itself is not called against the database until later, and it avoids the use of a regex.
public static IQueryable<T> WhereLike<T>(this IQueryable<T> source, Expression<Func<T, string>> valueSelector, string value, char wildcard)
{
return source.Where(BuildLikeExpression(valueSelector, value, wildcard));
}
public static IEnumerable<T> WhereLike<T>(this IEnumerable<T> source, Expression<Func<T, string>> valueSelector, string value, char wildcard)
{
return source.AsQueryable().WhereLike(valueSelector, value, wildcard);
}
private static Expression<Func<T, bool>> BuildLikeExpression<T>(Expression<Func<T, string>> valueSelector, string value, char wildcard)
{
if (valueSelector == null) throw new ArgumentNullException("valueSelector");
var method = GetLikeMethod(value, wildcard);
value = value.Trim(wildcard);
var body = Expression.Call(valueSelector.Body, method, Expression.Constant(value));
var parameter = valueSelector.Parameters.Single();
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
private static MethodInfo GetLikeMethod(string value, char wildcard)
{
var methodName = "Equals";
var textLength = value.Length;
value = value.TrimEnd(wildcard);
if (textLength > value.Length)
{
methodName = "StartsWith";
textLength = value.Length;
}
value = value.TrimStart(wildcard);
if (textLength > value.Length)
{
methodName = (methodName == "StartsWith") ? "Contains" : "EndsWith";
}
var stringType = typeof(string);
return stringType.GetMethod(methodName, new[] { stringType });
}
Usage:
// example data set
var data = new List<Person> {
new Person{FirstName="John", LastName="Smith"},
new Person{FirstName="Jane", LastName="Doe"}
};
data.WhereLike(x=>x.FirstName, "John", "%"); // returns John Smith
data.WhereLike(x=>x.FirstName, "J%", "%"); // returns John Smith and Jane Smith