1
private IQueryable<Customer> FilterResult(string search, List<Customer> dtResult, List<string> columnFilters)
        {
            IQueryable<Customer> results = dtResult.AsQueryable();

            results = results.Where(p => 
                (
                    search == null || 
                    (
                        p.Name != null && p.Name.ToLower().Contains(search.ToLower()) 
                        || p.City != null && p.City.ToLower().Contains(search.ToLower())
                        || p.Postal != null && p.Postal.ToLower().Contains(search.ToLower()) 
                        || p.Email != null && p.Email.ToLower().Contains(search.ToLower()) 
                        || p.Company != null && p.Company.ToLower().Contains(search.ToLower()) 
                        || p.Account != null && p.Account.ToLower().Contains(search.ToLower())
                        || p.CreditCard != null && p.CreditCard.ToLower().Contains(search.ToLower())
                    )
                ) 
                && (columnFilters[0] == null || (p.Name != null && p.Name.ToLower().Contains(columnFilters[0].ToLower())))
                && (columnFilters[1] == null || (p.City != null && p.City.ToLower().Contains(columnFilters[1].ToLower())))
                && (columnFilters[2] == null || (p.Postal != null && p.Postal.ToLower().Contains(columnFilters[2].ToLower())))
                && (columnFilters[3] == null || (p.Email != null && p.Email.ToLower().Contains(columnFilters[3].ToLower())))
                && (columnFilters[4] == null || (p.Company != null && p.Company.ToLower().Contains(columnFilters[4].ToLower())))
                && (columnFilters[5] == null || (p.Account != null && p.Account.ToLower().Contains(columnFilters[5].ToLower())))
                && (columnFilters[6] == null || (p.CreditCard != null && p.CreditCard.ToLower().Contains(columnFilters[6].ToLower())))
                );

            return results;
        }

This is the method which I am using for datatable filter , Here my question is can I make it as generic ? I feel it can be using reflection. But does that affect to performance as well ?

thx in advance..

I have done till it so far :

private IQueryable<T> FilterResult<T>(string search, IQueryable<T> dtResult, List<string> columnFilters)
    {
        IQueryable<T> results = dtResult;

        Type typeParameterType = typeof(T); // this will give me the class detail which I have passed 
    // 1 How to extract all property of this class to use in where clause
    // 2 How I can use it either dynamic linq , foreach or any possible solution. 
     //1
    PropertyInfo[] properties = typeParameterType.GetProperties(BindingFlags.Public | BindingFlags.Instance);


    foreach (var item in properties)
        {
// This will be changed after some validation logic
            string predicate = item.Name + " = " + search;
            results = results.Where(predicate);
        }


        return results;
    }
user5206903
  • 146
  • 1
  • 16
  • Irrelevant to your question but you should see this for the comparisons: http://stackoverflow.com/questions/444798/case-insensitive-containsstring – Rotem Sep 19 '15 at 09:18
  • Why would you want it as generic method? – Roman Dibikhin Sep 19 '15 at 09:19
  • Also, generic over what parameter? `Customer`? How exactly would that work? The method seems to be explicitly using all the fields of `Customer`. – Rotem Sep 19 '15 at 09:19
  • I am writing MVC helper for datatable so that I can use in my all project with all type of class.. and reduce the code to initialize grid control – user5206903 Sep 19 '15 at 09:20
  • 2
    I don't think he means generic as what introduced in `System.Collections.Generic`, he mean some kind of generalizing / shortening his code without having to access and check all properties of `p`. – Hopeless Sep 19 '15 at 09:21
  • To generalize the method, you would need some way of reducing `Customer` or any other such class to an ordered collection of `string` representing its fields, e.g. make `Customer` inherit from some `IFilterable` interface, which has a `GetFilterStrings` method. As @Hopeless mentions this doesn't seem to be related to generics. – Rotem Sep 19 '15 at 09:22
  • Use [`QueryBuilder`](https://msdn.microsoft.com/en-us/library/vstudio/bb896238(v=vs.100).aspx). – Sergey Kalinichenko Sep 19 '15 at 09:25
  • This is there, It will work fine when I have concrete class . but how to deal when I use class which is passed as T and it can be any class like school, teacher , product or customer. – user5206903 Sep 19 '15 at 09:30
  • Generics are suitable for when you write code which doesn't really care what the passed parameter is, e.g. collections. In your case the method cares greatly about the contents of the passed class. – Rotem Sep 19 '15 at 09:42
  • for your first requirement : //1 PropertyInfo[] properties = typeParameterType.GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var item in properties) { } – Kaushik Thanki Sep 19 '15 at 09:55
  • looks like you have to play with reflection to get all string properties. The performance is of course not good as what you have for each concrete type but it may be still acceptable depending your actual data, so just try it. BTW how do you get the `columnFilters` list? I would not pass in such a list of strings. If you can change that design (not depending someone else), you should do change it. Also what is actual input type of `dtResult`? If is just purely `IEnumerable`, it will be easier, otherwise `IQueryable` requires some Expression as predicate. – Hopeless Sep 19 '15 at 10:02
  • @Hopeless My idea was to apply search for passed columns as I may showing only some columns in data table.. I haven't use it till now .. but I partially got success for where clause.. check my updated question in some time – user5206903 Sep 19 '15 at 11:09
  • how could `Where` accept a string as predicate? you use some extension library? – Hopeless Sep 19 '15 at 11:13

1 Answers1

1

I'm not familiar (and don't intend to use) dynamic LINQ. In this case you don't really need such thing. As I said I would not pass in a list of string as column filters, it makes code longer and the order can matter and cause hard-to-debug issues. Here is the code I've just come up with, you can try and I'm not sure it works but if any please let me know:

private IEnumerable<T> FilterResult<T>(string search, IQueryable<T> dtResult, params ColumnFilter<T>[] filters)
{
   var propGetters = typeof(T).GetProperties().Where(p => p.PropertyType == typeof(string))
                              .Select(p => new Func<object,string>((item) => ((p.GetValue(item, null) as string) ?? "").ToLower())).ToList();   
   if(!string.IsNullOrEmpty(search)) {
      Func<T,bool> predicate = e => propGetters.Aggregate(false, (c,o) => c || o(e));
      dtResult = dtResult.Where(predicate).AsQueryable();
   }
   return filters.Aggregate(dtResult.AsEnumerable(), (c,e) => c.Where(o => e.IsOK(o));        
}
public class ColumnFilter<T> {
   public ColumnFilter(Func<T,string> selector, string term = ""){
     PropertySelector = selector;
     Term = term;
   }
   public Func<T,string> PropertySelector {get;set;}
   public string Term {get;set;}
   public bool IsOK(T item) {
     return PropertySelector(item).Contains(Term);
   }
}

Usage:

FilterResult(yourSearch, yourDtResult, new ColumnFilter(e=>e.City, "someCitySearch"), 
                                  new ColumnFilter(e=>e.Postal, "somePostalSearch"), ...);

If you still want to stick to List for columnFilters, the code can be modified but it will be surely longer.

Hopeless
  • 4,397
  • 5
  • 37
  • 64
  • Note: If `dtResult` is actually an `IQueryable` (***querying against database*** not local collection), then it won't work, some exception will throw. Then we have to build `Expression` tree which is fairly complicated. You can try searching more on that or stick with dynamic LINQ - which I can't help :) – Hopeless Sep 19 '15 at 11:45