Страницы

Поиск по вопросам

вторник, 30 октября 2018 г.

Динамическое построение Linq запроса

public class BaseEntity { public int Id {get;set;} public DateTime Created {get;set;} public bool IsDeleted {get;set;} }
Думаю данные свойства есть почти в каждой сущности, и довольно часто Возникает необходимость получить список за период, т.е. что то типа такого:
var start = new DateTime(2015, 11, 01); var stop = new DateTime(2015, 11, 30); var results = context.Entity.Where(x=>x.Created>=start&&x.Created<=stop).ToList()
но довольно часто таких сущностей несколько и плодить по сути однообразный код крайне не хочется, другой пример получить строки где какое то значение равно заданному, т.е. что то типа этого:
var arg = true; var results = context.Entity.Where(x=>x.IsDeleted==arg).ToList();
и это встречается тоже довольно часто ну и т.д.
т.е. хочется что то типа этого:
var start = new DateTime(2015, 11, 01); var stop = new DateTime(2015, 11, 30); var results = context.Entity.Beetwen("Created",start,stop).ToList();
public static class BaseEntityExtensions { //где field это имя поля по которому надо фильтровать записи public static IQueryable Beetwen(this IQueryable src, string field, DateTime start, DateTime stop) { //Здесь надеюсь пока неизвестная мне *магия* } }
Помогите реализовать метод Beetwen


Ответ

Where метод для IQueryable принимает Expression поэтому его можно собрать самостоятельно
public static IQueryable Beetwen(this IQueryable src, string field, DateTime start, DateTime stop) where T: BaseEntity { var p = Expression.Parameter(typeof(T)); // Param_0 var prop = Expression.PropertyOrField(p, field); //Param_0.field var startExpr = Expression.Constant(start); var stopExpr = Expression.Constant(stop);
var firstCondition = Expression.GreaterThanOrEqual(prop, startExpr); // Param_0.field >= start var secondCondition = Expression.LessThanOrEqual(prop, stopExpr); // Param_0.field <= stop
var fullExpression = Expression.AndAlso(firstCondition, secondCondition); // Param_0.field >= start && Param_0.field <= stop
var func = Expression.Lambda>(fullExpression,p); // Param_0 => Param_0.field >= start && Param_0.field <= stop
return src.Where(func); }
Методы которые использовались:
Expression.Parameter Expression.PropertyOrField Expression.Constant Expression.GreaterThanOrEqual Expression.LessThanOrEqual Expression.AndAlso Expression.Lambda
Альтернативным подходом может быть использование библиотеки DynamicLinq (есть в NuGet) в ней можно передавать в функции не Expression, а строку
src.Where(string.Format("{0} >= @0 && {0} <= @1", field), start, stop);
По мотивам ответа @Vlad, только используя уже переданное выражение
public static IQueryable Beetwen(this IQueryable src, Expression> propertyExpression, DateTime from, DateTime to) { var fromCondition = Expression.GreaterThanOrEqual(propertyExpression.Body, Expression.Constant(from)); var toCondition = Expression.LessThanOrEqual(propertyExpression.Body, Expression.Constant(to));
return src.Where(Expression.Lambda>(Expression.And(fromCondition, toCondition), propertyExpression.Parameters.First())); }
в использовании: var res = query.Beetwen(_ => _.Date, from, to);
И еще вариант, но не такой интересный как предыдущие
public static IQueryable Beetwen(this IQueryable src, Expression> propertyExpression, DateTime from, DateTime to) { Expression> func = d => d >= from && d <= to;
return src.Where(Expression.Lambda>(Expression.Invoke(func, propertyExpression.Body), propertyExpression.Parameters.First())); }
в использовании: var res = query.Beetwen(_ => _.Date, from, to);

немного обобщенный вариант
public static IQueryable Beetwen(this IQueryable src, Expression> propertyExpression, U from, U to) { var fromCondition = Expression.GreaterThanOrEqual(propertyExpression.Body, Expression.Constant(from)); var toCondition = Expression.LessThanOrEqual(propertyExpression.Body, Expression.Constant(to));
return src.Where(Expression.Lambda>(Expression.And(fromCondition, toCondition), propertyExpression.Parameters.First())); }
в использовании: var res = query.Beetwen(_ => _.Date, from, to);

Комментариев нет:

Отправить комментарий