Searching each property value of an IQueryable collection of T against the value of a search query. How do I test for NOT NULL and CONTAINS together?

StackOverflow https://stackoverflow.com/questions/22945782

سؤال

I am trying search each property value of an IQueryable collection of T against the value of a search query. I have the following function and would like to know how do I ALSO test for NOT NULL and CONTAINS together?

private Expression<Func<T, bool>> PropertySearch
{
  get
  {
     // Object that is passed to the lambda expression
     ParameterExpression instance = Expression.Parameter(typeof(T), "val");
     Expression whereExpr = Expression.Constant(true); // default is val => True

     var _properties = typeof(T).GetProperties();
     foreach (var prop in _properties)
     {
        var query = _httpRequest["query"].ToLower();
        var property = Expression.Property(instance, prop);
        var toStringCall = Expression.Call(Expression.Call(
            property,
            "ToString", 
            new Type[0]), 
            typeof(string).GetMethod("ToLower", new Type[0]));
        whereExpr = Expression.And(whereExpr, 
            Expression.Call(toStringCall, typeof(string).GetMethod("Contains"), 
            Expression.Constant(query)));
     }
     return Expression.Lambda<Func<T, bool>>(whereExpr, instance);
}}
هل كانت مفيدة؟

المحلول

I have created a search extensions nuget package that performs this type of check. For your example I would do something like the following.

Note, this is without an IDE so may have some errors

/* *** Start: These can be made private reaonly fields ***/
var comparisonExpr = Expression.Constant(StringComparison.OrdinalIgnoreCase);
var zeroExpression = Expression.Constant(0)
var nullExpression = Expression.Constant(null)
MethodInfo IndexOfMethod = typeof(string).GetMethod("IndexOf", new[] { typeof(string), typeof(StringComparison) });  
/* *** End ***/

Expression finalExpression = null
ParameterExpression instance = Expression.Parameter(typeof(T), "val");
var _properties = typeof(T).GetProperties();
var query = _httpRequest["query"].ToLower();
var queryExpr = Expression.Constant(query);

foreach (var prop in _properties)
{
    //Get property
    var propertyExpr = Expression.Property(instance, prop);
    //Get property as string
    var propStringExpr = Expression.Call(property, "ToString", new Type[0]);
    //Perform IndexOf call
    var indexOfExpr = Expression.Call(propStringExpr, 
                                       IndexOfMethod, 
                                       queryExpr, 
                                       comparisonExpr);

    // Check index of is greater than or equal to zero
    var containsExpr = Expression.GreaterThanOrEqual(containsExpr, zeroExpression);

    if(finalExpression == null)
    {
        finalExpression = containsExp;
    }
    else
    {
        finalExpression = Expression.AndAlso(containsExpr);
    }
}

return Expression.Lambda<Func<T, bool>>(finalExpression, instance);

I've removed the need for ToLower() and instead used IndexOf with a string comparison type

If you want to see how I have achieved similar functionality, take a look at NinjaNye.SearchExtensions on Github

https://github.com/ninjanye/SearchExtensions

If you wanted to search a collection of IQueryable you could use NinjaNye.SearchExtensions as follows

string query = _httpRequest["query"];
var result = data.SearchAll().Containing(query);

This will search all string properties (not all properties as you have above) and return just those where any property mathes the search term.

Hope this helps

نصائح أخرى

You could probably use PredicateBuilder so you don't have to mess with expression trees yourself.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top