Development With A Dot

Blog on development in general, and specifically on .NET

Sponsors

News

My Friends

My Links

Permanent Posts

Portuguese Communities

Dynamic Filtering

Continuing my previous posts on dynamic LINQ, now it's time for dynamic filtering. For now, I'll focus on string matching.

There are four standard operators for string matching, which both NHibernate, Entity Framework and LINQ to SQL recognize:

  • Equals
  • Contains
  • StartsWith
  • EndsWith

So, if we want to apply filtering by one of these operators on a string property, we can use this code:


public enum MatchType
{
	StartsWith = 0,

	EndsWith = 1,

	Contains = 2,

	Equals = 3
}

public static List<T> Filter<T>(IEnumerable<T> enumerable, String propertyName, String filter, MatchType matchType)
{
	return (Filter(enumerable, typeof(T), propertyName, filter, matchType) as List<T>);
}

public static IList Filter(IEnumerable enumerable, Type elementType, String propertyName, String filter, MatchType matchType)
{
	MethodInfo asQueryableMethod = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(m => (m.Name == "AsQueryable") && (m.ContainsGenericParameters == false)).Single();
	IQueryable query = (enumerable is IQueryable) ? (enumerable as IQueryable) : asQueryableMethod.Invoke(null, new Object [] { enumerable }) as IQueryable;
	MethodInfo whereMethod = typeof(Queryable).GetMethods(BindingFlags.Public | BindingFlags.Static).Where(m => m.Name == "Where").ToArray() [ 0 ].MakeGenericMethod(elementType);

	MethodInfo matchMethod = typeof(String).GetMethod
	(
		(matchType == MatchType.StartsWith) ?
			"StartsWith" :
			(matchType == MatchType.EndsWith) ?
				"EndsWith" :
				(matchType == MatchType.Contains) ?
					"Contains" : 
						"Equals",
		new Type [] { typeof(String) }
	);

	PropertyInfo displayProperty = elementType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
	MemberExpression member = Expression.MakeMemberAccess(Expression.Parameter(elementType, "n"), displayProperty);
	MethodCallExpression call = Expression.Call(member, matchMethod, Expression.Constant(filter));
	LambdaExpression where = Expression.Lambda(call, member.Expression as ParameterExpression);

	query = whereMethod.Invoke(null, new Object [] { query, where }) as IQueryable;

	MethodInfo toListMethod = typeof(Enumerable).GetMethod("ToList", BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(elementType);
	IList list = toListMethod.Invoke(null, new Object [] { query }) as IList;

	return (list);
}

var list = new [] { new { A = "aa" }, new { A = "aabb" }, new { A = "ccaa" }, new { A = "ddaadd" } };

var contains = Filter(list, "A", "aa", MatchType.Contains);
var endsWith = Filter(list, "A", "aa", MatchType.EndsWith);
var startsWith = Filter(list, "A", "aa", MatchType.StartsWith);
var equals = Filter(list, "A", "aa", MatchType.Equals);

Perhaps I'll write some more posts on this subject in the near future.

Bookmark and Share

Comments

Raghuraman said:

Brilliant Again !!! Thx for Sharing !!!

# April 10, 2010 10:25 PM

Andy Thomas said:

Excellent article solved a showstopper that had me stumped for weeks. Any idea how to make it case insensitive

# May 27, 2010 10:00 AM

Ricardo Peres said:

Andy:

Since it uses the SQL LIKE operator, it should be case-insensitive.

# May 27, 2010 1:54 PM