How to extend this LINQ List<>.Contains to work with any Property using Reflection?

dynamic-linq linq nhibernate reflection

Question

I have the following snippet that I currently use to run a .Contains() with a list of Ids passed as a comma separated list from users. This code works perfectly and the data is filtered exactly as I want it to be:

// Handle id in() statements explicitly, dynamic expression can't parse them
var idIn = new Regex("id in ?(.*)", RegexOptions.IgnoreCase);
if (idIn.IsMatch(predicate))
{
    Match list = new Regex(@"in ?\((.*)\)", RegexOptions.IgnoreCase).Match(predicate);
    string ins = list.Groups[1].ToString();

    // Split ins and store as List<>
    List<int> splitValues = ins.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(i => Convert.ToInt32(i)).ToList();

    return source.Where(u => splitValues.Contains(u.Id));
}

I want to be able to use this same idea, except with ANY property of the u object using reflection. I had a version of this working at some point, but cannot for the life of me figure out what has changed or why it stopped working. Here is the version I have that I cannot get working again:

Match splitIn = new Regex(@"([a-zA-Z0-9\.]*) IN ?\((.*)\)", RegexOptions.IgnoreCase).Match(predicate);
string property = splitIn.Groups[1].ToString();
string ins = splitIn.Groups[2].ToString().Trim(new[] {'\'', '"'}); // Trim off separator quotes

List<string> splitValues = ins.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList();
for (int i = 0; i < splitValues.Count; i++)
{
    splitValues[i] = splitValues[i].Trim(new[] {'\'', '"'});
}

Expression<Func<U, bool>> contains = u => ListContainsProperty(u, splitValues, property);
return source.Where(contains);

private static bool ListContainsProperty<U>(U u, ICollection<string> list, string property)
{
    string[] split = property.Split(new[] {"."}, StringSplitOptions.RemoveEmptyEntries);
    object value = split.Aggregate<string, object>(u, (current, prop) => current.GetType().GetProperty(prop).GetValue(current, null));

    return list.Contains(value.ToString());
}

As I said I once had SOME version of this working, but cannot figure out what has changed. Is there something blatantly obvious that I am missing that would help me get this functional again?

Edit: As far as I can tell the ListContainsProperty method is never actually running. Adding a "throw new Exception()" does nothing. I just get the full unfiltered list back.

1
2
6/8/2012 8:19:20 PM

Popular Answer

I think the underlying problem is using "Expression" you need to compile an Expression.

For example in your code

Expression<Func<U, bool>> contains = u => ListContainsProperty(u, splitValues, property);

is data and not a function. In order to use it you need to compile it.

Func<U, bool> compiled = contains.Compile();

"compiled" variable will call the "ListContainsProperty" method.

0
2/21/2013 7:32:38 AM


Related Questions





Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow