C# Передайте выражение в качестве аргумента и используйте PredicateBuilder
Я хочу динамически создавать предложения запросов LinqToSQL и EntityFramework, используя объект "критерии" и, при необходимости, используя C# в Nutshell PredicateBuilder ( http://www.albahari.com/nutshell/predicatebuilder.aspx).
Просто чтобы быть ясно - я не понимаю, как создавать выражения C#; о)
Оператор и значение фильтра определены как свойство моего класса "критерий запроса" следующим образом:
public class QueryCriteria
{
public DateTimeFilter DateTimeCompleted { get; set; }
}
"Фильтр" определяется как:
public class DateTimeFilter
{
public FilterOperator FilterOperator { get; set; }
public DateTime FilterToken { get; set; }
}
И "оператор фильтра" - это простое перечисление C#:
public enum FilterOperator : int
{
ExactMatch = 1,
BeginsWith = 2,
Contains = 3,
EndsWith = 4,
GreaterThan = 5,
GreaterThanOrEqualTo = 6,
LessThan = 7,
LessThanOrEqualTo = 8,
}
Я хочу динамически создавать запросы LinqToSQL и EntityFramework в моих репозиториях, используя такой подход, как:
if (criteria.DateTimeCompleted != null)
{
var predicate = PredicateBuilder.True(query);
switch (criteria.DateTimeCompleted.FilterOperator)
{
case FilterOperator.ExactMatch:
predicate = predicate.And(p => string.Equals(p.DateTimeCompleted.Value, criteria.DateTimeCompleted.FilterToken));
break;
case FilterOperator.GreaterThan:
predicate = predicate.And(p => p.DateTimeCompleted.Value > criteria.DateTimeCompleted.FilterToken);
break;
case FilterOperator.GreaterThanOrEqualTo:
predicate = predicate.And(p => p.DateTimeCompleted.Value >= criteria.DateTimeCompleted.FilterToken);
break;
case FilterOperator.LessThan:
predicate = predicate.And(p => p.DateTimeCompleted.Value < criteria.DateTimeCompleted.FilterToken);
break;
case FilterOperator.LessThanOrEqualTo:
predicate = predicate.And(p => p.DateTimeCompleted.Value <= criteria.DateTimeCompleted.FilterToken);
break;
default:
throw new InvalidOperationException("Can't use the specified filter operator on a DateTime.");
}
query = query.Where(predicate);
}
Все вышеперечисленное работает, но я хочу, чтобы мой код был СУХИМ, и когда я добавляю другое свойство к моим критериям (например, DateTimeStarted), я не хочу, чтобы мой репозиторий дублировал код, приведенный выше, непосредственно для нового свойства.
Поэтому я хочу упростить приведенный выше код до чего-то вроде:
query = query.Where(criteria.DateTimeCompleted.GetPredicate(query, p => p.DateTimeCompleted.Value));
Возможно ли это и если да, то как?
1 ответ
Это определенно возможно (и в PredicateBuilder нет необходимости). Все, что вам нужно, это сопоставить поддерживаемые FilterOperator
к соответствующему Expression
,
Вот пользовательский метод расширения, который делает это:
public static class QueryableExtensions
{
public static IQueryable<T> Where<T>(this IQueryable<T> source, DateTimeFilter filter, Expression<Func<T, DateTime>> target)
{
if (filter == null) return source;
var left = target.Body;
var right = Expression.Constant(filter.FilterToken);
Expression condition;
switch (filter.FilterOperator)
{
case FilterOperator.ExactMatch:
condition = Expression.Equal(left, right);
break;
case FilterOperator.GreaterThan:
condition = Expression.GreaterThan(left, right);
break;
case FilterOperator.GreaterThanOrEqualTo:
condition = Expression.GreaterThanOrEqual(left, right);
break;
case FilterOperator.LessThan:
condition = Expression.LessThan(left, right);
break;
case FilterOperator.LessThanOrEqualTo:
condition = Expression.LessThanOrEqual(left, right);
break;
default:
throw new InvalidOperationException("Can't use the specified filter operator on a DateTime.");
}
var predicate = Expression.Lambda<Func<T, bool>>(condition, target.Parameters);
return source.Where(predicate);
}
}
и пример использования:
query = query.Where(criteria.DateTimeCompleted, p => p.DateTimeCompleted.Value);