Страницы

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

четверг, 19 декабря 2019 г.

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

#c_sharp #entity_framework #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
    


Ответы

Ответ 1



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);

Ответ 2



Я бы посоветовал использовать решение, которое поддается рефакторингу. И передавать в метод не имя свойства, а выражение. public static IQueryable Beetwen(this IQueryable src, Expression> propertyExpression, DateTime from, DateTime to) { var parameterExpr = Expression.Parameter(typeof(T)); var parameterizedPropertyExpr = Expression.PropertyOrField(parameterExpr, ((MemberExpression)propertyExpression.Body).Member.Name); var fromCondition = Expression.GreaterThanOrEqual(parameterizedPropertyExpr, Expression.Constant(from)); var toCondition = Expression.LessThanOrEqual(parameterizedPropertyExpr, Expression.Constant(to)); return src.Where(Expression.Lambda>(Expression.And(fromCondition, toCondition), parameterExpr)); } В этом случае метод можно вызывать вот так: var res = query.Beetwen(_ => _.Date, from, to);

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

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