#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 IEnumerableNames { 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.
Комментариев нет:
Отправить комментарий