Продолжаю эксперименты с jqGrid. На этот раз дотянулись руки до создания динамического запроса.
В прошлой статье удалось сократить сигнатуру метода получения данных ProductData. Но остался большой кусок кода отвечающий за создание запроса на основании введенных пользователем данных в поля фильтра таблицы. Все бы ничего, но кодить такое совершенно не доставляет удовольствия.
Для начала необходимо скачать исходники проекта. Фактически это итоговый проект того что было сделано в прошлой статье.
Откроем ProductController.cs, и внимательнее приглядимся к методу ProductData, а точнее к той части где накладываются фильтры на коллекцию:
if (_search)
{
if (null != searchProduct.ProductId)
list = list.Where(x => searchProduct.ProductId == x.ProductId);
if (!String.IsNullOrEmpty(searchProduct.ProductName))
list = list.Where(x => x.ProductName == searchProduct.ProductName);
if (!String.IsNullOrEmpty(searchProduct.Category))
list = list.Where(x => x.Category == searchProduct.Category);
if (!String.IsNullOrEmpty(searchProduct.Supplier))
list = list.Where(x => x.Supplier == searchProduct.Supplier);
if (null != searchProduct.UnitPrice)
list = list.Where(x => x.UnitPrice == searchProduct.UnitPrice);
if (null != searchProduct.UnitsInStock)
list = list.Where(x => x.UnitsInStock == searchProduct.UnitsInStock);
if (!String.IsNullOrEmpty(searchProduct.EnglishName))
list = list.Where(x => x.EnglishName == searchProduct.EnglishName);
}
Первое что видно что идет поиск свойств у которых есть значения. В этом нам поможет Reflection. Результатом работы следующего кода является список свойств с значениями.
var props = filterValues.GetType().GetProperties()
.Where(propertyInfo =>
propertyInfo.GetValue(filterValues, null) != null)
.ToList();
Теперь у нас есть список свойств, используя который можно без особого труда создать лямбда выражения и отфильтровать список. В исходное лямбда-выражение выглядят следующим образом:
x => x.ProductName == searchProduct.ProductName
Где x — аргумент, свойства которого используются в выражении,
searchProduct.ProductName — значение для правой части условия.
x.ProductName — свойство объекта, используемое в условии.
Expression<Func> — тип этого лямбда-выражения.
В итоге, для получения лямбда-выражения понадобиться имя свойства, значение фильтра и тип объекта.
Собственно:
public static Expression<Func<T, bool>> CreatePredicate<T>(string propertyName, object propertyValue)
{
var argument = Expression.Parameter(typeof (T),"x");
var property = Expression.Convert(
Expression.Property(argument, propertyName),
propertyValue.GetType());
var value = Expression.Constant(propertyValue);
var body = Expression.Equal(property, value);
return Expression.Lambda<Func<T, bool>>(body,argument);
}
Стоит пояснить вот эту строчку:
var property = Expression.Convert(
Expression.Property(argument, propertyName),
propertyValue.GetType());
Если тип исходного свойства класса не привести к типу аргумента, то обязательно возникнет исключение:
System.InvalidOperationException: Двоичный оператор Equal не определен для типов «System.Nullable`1[System.Decimal]» и «System.Decimal».
На основе этого я создал метод расширения WhereFilter для IQueryable. Итоговые исходники проекта можно скачать тут.
Related posts: