I've a list of Object such as:
var data = ctx.Clinics.AsNoTracking().OrderBy(p => p.Name).Select(p => new
{
ID = p.ID,
Name = p.Name,
Region = p.Regions.Description,
City = p.City,
Address = p.Address,
Phone = p.Phone,
InterestPassive = p.InterestPassive,
InterestGlad = p.InterestGlad,
Gross = p.Activities.Where(e => e.Converted.HasValue && e.Converted.Value && e.DateActivity.HasValue).Sum(s => (decimal?)s.FirstEstimate) ?? 0,
DiscountApplied = p.Activities.Where(e => e.Converted.HasValue && e.Converted.Value && e.DateActivity.HasValue && e.Discount != null).Sum(e => (e.FirstEstimate / 100) * e.Discount) ?? 0,
}).ToList();
which is a result set for a DataTable.
On this list, I'd like to apply a filter on every property of the list which "contains" a input string (such as free textbox that will search everywhere in a grid):
string searchTerm = Request.Form.GetValues("search[value]").FirstOrDefault();
Is there a way on LINQ to search a value for every property within a list of Objects?
If you don't mind using Reflection, and you intend to search all public properties, you can use the following extensions to do a Contains
search against every public property:
public static class IQueryableEFExt {
#region Predicates
// findTerm - string that must be contained by any property
// r => s => r.AllPropertyValues().Any(pv => pv.ToString().Contains(findTerm))
public static Func<T, bool> IsAnyContains<T>(string findTerm) => r => r.AnyPropertyContains(findTerm);
#endregion
#region Where
// findTerm - string that must be contained by any property
// dbq.Where(r => r.AllPropertyValues().Any(pv => pv.ToString().Contains(findTerm)))
public static IEnumerable<T> WhereAnyContains<T>(this IEnumerable<T> src, string findTerm) => src.Where(IsAnyContains<T>(findTerm));
#endregion
#region OrderBy
// findTerm - string that must be contained by any property
// dbq.OrderBy(r => r.AllPropertyValues().Any(pv => pv.ToString().Contains(findTerm)) ? 1 : 2)
public static IOrderedEnumerable<T> OrderByAnyContains<T>(this IEnumerable<T> src, string findTerm)
=> src.OrderBy(r => IsAnyContains<T>(findTerm)(r) ? 1 : 2);
// findTerm - string that must be contained by any property
// dbq.OrderByDescending(r => r.AllPropertyValues().Any(pv => pv.ToString().Contains(findTerm)) ? 1 : 2)
public static IOrderedQueryable<T> OrderByLikeAnyDescending<T>(this IQueryable<T> dbq, string findTerm)
=> dbq.OrderByDescending(r => IsAnyContains<T>(findTerm)(r));
#endregion
}
This code depends on a custom extension method:
public static bool AnyPropertyContains<T>(this T anObject, string substr) => typeof(T).GetProperties().Any(pf => pf.GetValue(anObject).ToString().Contains(substr));
If a little performance is important to you, you could refactor to cache the return value of GetProperties()
to not do that lookup every time.
You would use it like:
var ans = data.WhereAnyContains(searchTerm);