#c_sharp #linq
Имеется модель EF:
public class OrderDbModel
{
public bool IsPayed { get; set; }
public string Name { get; set; }
public DateTime Created { get; set; }
public bool IsDeleted { get; set; }
}
Ей соответствует доменная модель:
public class Order
{
public bool IsPayed { get; set; }
public string Name { get; set; }
}
Чтобы получить список доменных моделей заказов из сервиса в слое бизнес-логики приложения,
предполагается использование следующего класса-фильтра:
public class OrderFilter
{
public bool GetUnpayed { get; set; }
public IEnumerable Names { get; set; }
}
Для получения списка доменных моделей по фильтру создан сервис, который использует
репозиторий:
public class OrderService
{
private readonly IOrderRepository _repository;
public OrderService(IOrderRepository repository)
{
_repository = repository;
}
public IEnumerable GetFilteredOrders(OrderFilter filter)
{
// как динамически сформировать выражение???
Expression> filterExpression = null;
var orders = _repository.Get(filterExpression);
return Mapper.Map, IEnumerable>(orders);
}
}
Собственно, сам вопрос - в комментариях в коде сервиса. Не получается динамически
сформировать filterExpression. Крайне интересен пример кода для данного конкретного
случая с подробным описанием. Имеются ли какие-то библиотеки, которые позволяют это
сделать с наименьшими трудозатратами?
PS: .NET Framework 4.6, C# 6.
Пример значений:
для OrderFilter со значениями { GetUnpayed = false, Names = new List { "Foo",
"Bar" } } должно плучиться выражение o => o.IsPayed && (o.Name == "Foo") || o.Name
== "Bar")
Ответы
Ответ 1
И так, вам нужен способ комбинировать выражения. Проще всего это сделать через операцию бета-редукции (т.е. подстановки фактического параметра на место формального). Для этого понадобится вот такой простейший визитор (visitor, "посетитель"): class Reducer : ExpressionVisitor { private readonly IDictionaryarguments; public Reducer(IDictionary arguments) { this.arguments = arguments; } protected override Expression VisitParameter(ParameterExpression node) { Expression result; if (arguments.TryGetValue(node, out result)) return result; return base.VisitParameter(node); } } Даже если вы не сталкивались с классом ExpressionVisitor ранее - тут все просто. Класс Reducer заменяет в выражении параметры (объекты класса ParameterExpression ) на соответствующие им во входном словаре, если находит их там. Остальные узлы выражения он не трогает. Теперь можно определить операцию подстановки. Тут все просто: формируем словарь параметров и передаем его классу Reducer после чего прогоняем через Reducer тело лямбда-функции: public static Expression BetaReduce(this LambdaExpression expr, params Expression[] args) { Debug.Assert(expr.Parameters.Count == args.Length); var mapping = new Dictionary (); for (int i=0; i > And (params Expression >[] items) => And(items.AsEnumerable()); public static Expression > And (IEnumerable >> items) { var p = Expression.Parameter(typeof(T)); var body = items.Select(item => item.BetaReduce(p)).Aggregate(Expression.AndAlso); return Expression.Lambda >(body, p); } Или комбинатор Or: public static Expression > Or (params Expression >[] items) => Or(items.AsEnumerable()); public static Expression > Or (IEnumerable >> items) { var p = Expression.Parameter(typeof(T)); var body = items.Select(item => item.BetaReduce(p)).Aggregate(Expression.OrElse); return Expression.Lambda >(body, p); } Теперь вам ничего не помешает сложить все ваши условия в список, после чего объединить их все через And: var conditions = new List >>(); if (filter.GetUnpayed) conditions.Add(x => !x.IsPayed); ... var filterExpression = ExpressionUtils.And(conditions); Если же вам не нравится работать с выражениями в "сыром" виде, можете попробовать библиотеку LinqKit.
Комментариев нет:
Отправить комментарий