how to select multi columns filter with lambda expression

Viewed 295

is there a short way, I don't want to use "if state" for every situation what do you think Description : type of query is IQueryable

public class OrderFilter{
     public string SearchValue { get => _searchValue.ToLower(); set => _searchValue = value; }
     public string[] SearchColumns { get; set; }
}
if (!string.IsNullOrWhiteSpace(orderFilter.SearchValue))
            
   if (orderFilter.SearchColumns.Contains("warehouse"))
       query = query.Where(x => x.Warehouse.Description.Contains(orderFilter.SearchValue));
   if (orderFilter.SearchColumns.Contains("date"))
      query = query.Where(x => x.Date.Contains(orderFilter.SearchValue));
   if (orderFilter.SearchColumns.Contains("warehouse") && orderFilter.SearchColumns.Contains("date"))
      query = query.Where(x => x.OrderNo.Contains(orderFilter.SearchValue)
                    || x.Warehouse.Description.Contains(orderFilter.SearchValue)
                    || x.Client.Description.Contains(orderFilter.SearchValue)
                    || x.Date.ToString().Contains(orderFilter.SearchValue)
                    || x.Salesman.Username.Contains(orderFilter.SearchValue)
                    );
            } 

enter image description here

3 Answers

Access nested properties with dynamic lambda using Linq.Expression found the answer I was looking for

public class LambdaHelper
    {
        public Expression<Func<TSource, bool>> MultiSearchOrder<TSource>(string[] columns, string value)
        {
            ParameterExpression parameter = Expression.Parameter(typeof(TSource), "x"); // x=>  . ... demektir. parametre
            MethodInfo containsMethod = typeof(String).GetMethod("Contains", new Type[] { typeof(String) });
            Expression dynamiclambda = null;
            MethodCallExpression call = null;
            foreach (var propertyName in columns)
            {

                MemberExpression propertyAccess = NestedExpressionProperty(parameter, propertyName);
                call = Expression.Call(propertyAccess, containsMethod, Expression.Constant(value));
                if (null == dynamiclambda)
                {
                    dynamiclambda = call;
                }
                else
                {
                    dynamiclambda = Expression.Or(dynamiclambda, call);
                }
            }
            Expression<Func<TSource, bool>> predicate = Expression.Lambda<Func<TSource, bool>>(dynamiclambda, parameter);

            return predicate;

        }
 
        private MemberExpression NestedExpressionProperty(Expression expression, string propertyName)
        {
            string[] parts = propertyName.Split('.');
            int partsL = parts.Length;

            return (partsL > 1)
                ?
                Expression.Property(
                    NestedExpressionProperty(
                        expression,
                        parts.Take(partsL - 1)
                            .Aggregate((a, i) => a + "." + i)
                    ),
                    parts[partsL - 1])
                :
                Expression.Property(expression, propertyName);
        }

I think you add one property (SearchKey) that property contains of all properties value that used for search in writing time and you using this property instead of OR for example :

 savingTime: SearhKey="OrderNo.value Warehouse.Description.Value ...."

 Searching Time:
 if (!string.IsNullOrWhiteSpace(orderFilter.SearchValue))
 {
     query = query.Where(x => 
    x.SearchKey.Contains(orderFilter.SearchValue) );
 }

you can do an extension o OrderFilter and reuse it :

public static bool HasAny<TSource>(this OrderFilter filter, IEnumerable<TSource> source)
{
    if(filter == null)
        throw new ArgumentNullException(nameof(filter));

    if(filter.SearchColumns == null)
        throw new ArgumentNullException(nameof(filter.SearchColumns));
    
    if(string.IsNullOrWhiteSpace(filter.SearchValue))
        throw new ArgumentNullException(nameof(filter.SearchValue));

    if(source == null)
        throw new ArgumentNullException(nameof(source));
    
    var properties = typeof(TSource).GetProperties().Where(x=> filter.SearchColumns.Any(s=> s.IndexOf(x.Name, StringComparison.InvariantCultureIgnoreCase) > 0));

    foreach(var item in source)
    {
        foreach(var property in properties)
        {
            var value = property.GetValue(item)?.ToString();
            
            if(value?.Equals(filter.SearchValue , StringComparison.InvariantCultureIgnoreCase) == true)
            {
                return true;
            }
        }

    }
    
    return false;
}

now you can do this :

var isValueExists = orderFilter.HasAny(query);
Related