Question

I am using below code for Generic Filter, any search text passed but the contains method is Case sensitive, how can I write to ignore case.

public static class QueryExtensions
{
    public static IQueryable<T> Filter<T>(this IQueryable<T> query, string search)    
    {           
        var properties = typeof(T).GetProperties().Where(p => 
                /*p.GetCustomAttributes(typeof(System.Data.Objects.DataClasses.EdmScalarPropertyAttribute),true).Any() && */
                p.PropertyType == typeof(String));        

        var predicate = PredicateBuilder.False<T>();
        foreach (var property in properties )
        {
           predicate = predicate.Or(CreateLike<T>(property,search));
        }
        return query.AsExpandable().Where(predicate);
    }
    private static Expression<Func<T,bool>> CreateLike<T>( PropertyInfo prop, string value)
    {       
        var parameter = Expression.Parameter(typeof(T), "f");
        var propertyAccess = Expression.MakeMemberAccess(parameter, prop);                    
        var like = Expression.Call(propertyAccess, "Contains", null, Expression.Constant(value,typeof(string)));

        return Expression.Lambda<Func<T, bool>>(like, parameter);       
    }

}
Était-ce utile?

La solution

Instead of calling String.Contains, call String.IndexOf with a case insensitive StringComparison parameter. Then compare its result with 0, with the Expression.GreaterThanOrEqual expression. You need to provide the extra parameter in your Expression.Call as an Expression.Constant.

You can decide to hardcode one of the case-insensitive StringComparison options, or export it as a parameter of the Filter method, allowing users to decide whether they want case-insensitive search or not.

You can do something like this:

    private static Expression<Func<T, bool>> CreateLike<T>(PropertyInfo prop, string value)
    {
        var parameter = Expression.Parameter(typeof(T), "f");
        var propertyAccess = Expression.MakeMemberAccess(parameter, prop);

        var indexOf = Expression.Call(propertyAccess, "IndexOf", null, Expression.Constant(value, typeof(string)),Expression.Constant(StringComparison.InvariantCultureIgnoreCase));
        var like=Expression.GreaterThanOrEqual(indexOf, Expression.Constant(0));
        return Expression.Lambda<Func<T, bool>>(like, parameter);
    }

or, with the StringComparison parameter

    private static Expression<Func<T, bool>> CreateLike<T>(PropertyInfo prop, 
        string value, 
        StringComparison comparison=StringComparison.InvariantCultureIgnoreCase)
    {
        var parameter = Expression.Parameter(typeof(T), "f");
        var propertyAccess = Expression.MakeMemberAccess(parameter, prop);

        var indexOf = Expression.Call(propertyAccess, "IndexOf", null, 
            Expression.Constant(value, typeof(string)),
            Expression.Constant(comparison));
        var like=Expression.GreaterThanOrEqual(indexOf, Expression.Constant(0));
        return Expression.Lambda<Func<T, bool>>(like, parameter);
    }

By using a default value for comparison you avoid creating two overloads for the same job.

Autres conseils

You could try using String.IndexOf instead.

string x,y = string.Empty;
x.IndexOf(y,0,x.Length, StringComparison.CurrentCultureIgnoreCase) > -1

As it has a StringComparison parameter.

This would return an integer

var like = Expression.Call(propertyAccess, "IndexOf", null, Expression.Constant(value, typeof(string)), Expression.Constant(StringComparison.CurrentCultureIgnoreCase,typeof(StringComparison)));

Please refer to the following code if you want to filter or search for a value from the list. In addition, it is a generic method that will help you filter any type of class or object from the list. It is working as a like clause in SQL such as (column1 like '%abc%' or column2 like '%abc%').

public static class Filter<T>
{
    public static Expression<Func<T, bool>> FilterExpression(string searchValue)
    {
        Expression finalExpression = Expression.Constant(false);
        var parameter = Expression.Parameter(typeof(T), "x");
        PropertyInfo[] propertyInfos = typeof(T).GetProperties();

            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                if (propertyInfo.PropertyType == typeof(string))
                {
                    var propertyExpn = Expression.Property(parameter, propertyInfo.Name.Trim().ToLower());
                    var containsExpn = Expression.Call(propertyExpn, "Contains", null, Expression.Constant(searchValue, typeof(string)), Expression.Constant(StringComparison.InvariantCultureIgnoreCase));
                    var nullCheckExpn = Expression.NotEqual(propertyExpn, Expression.Constant(null, typeof(string)));
                    var andAlsoExpn = Expression.AndAlso(nullCheckExpn, containsExpn);
                    finalExpression = Expression.Or(finalExpression, andAlsoExpn);
                }
            }
            var rowFilterLambdaExpression = Expression.Lambda<Func<T, bool>>(finalExpression, new ParameterExpression[] { parameter });
            return rowFilterLambdaExpression;
    }
}

Usage E.g.,

var result = 
 dataItems.Where(Filter<T>.FilterExpression(model.FilterValue).Compile()).ToList();

It's probably simplest to convert both parameters to upper case first (upper case conversions are optimized better than lower case ones in .NET). You can then do your comparison.

Upper case conversion can be done like this:

var expression = Expression.Call(property, typeof(string).GetMethod("ToUpperInvariant", System.Type.EmptyTypes));
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top