2016-02-27 2 views
0

Я пытаюсь написать анализируемую библиотеку выражений текста в формате EF. Представьте текстовое поле, в котором пользователь вводит список целых чисел с разделителями-запятыми. Я разберу & разобрать его и сделать из него массив int. Но тогда мне нужно создать выражение для этого один из моих требований является взять ctx => ctx.User.Receipts.Select(x => x.ReceiptID) и список Квитанция идентификаторов (Интс) и генерировать Expression, что делает что-то вроде этого:Преобразование выражения <Func <TEntity, IEnumerable <TProperty> >> valueSelector, TValue [] values ​​to Expression <Func <TElement, bool >>

ctx.User.Where(x => x.Receipts.Any(y => listOfIds.Contains(y.ReceiptId));

Я думаю, что это подпись будет что-то вроде этого:

Expression<Func<TEntity, bool>> CreateListExpression(Expression<Func<TEntity, IEnumerable<TProperty>>> accessor, TProperty[] constantValues)

и использование образца будет что-то вроде этого:

ctx => ctx.User.Where(CreateListExpression(x => x.Receipts.Select(y => y.ReceiptId), new int[] { 1, 2, 3, 4, 5 });

... но я гибкий с 1 ограничением. В SQL, я искал что-то сравнимого с этим (с точки зрения того, насколько эффективно я извлечения данных & убедившись, что работа happing SQL-сторона):

select u.* 
from Users u 
join Receipts on r.UserID = u.UserID 
where r.ReceiptID in (1, 2, 3, 4, 5) 

ответ

0

На самом деле то, что вы просите за это довольно легко, как только вы понимаете, что

(Parent p) => p.Children.Any(c => filter(c.Property)) 

эквивалентно

(Parent p) => p.Children.Select(c => c.Property).Any(v => filter(v)) 

Так что функция может быть, как это

Expression<Func<TEntity, bool>> CreateListExpression<TEntity, TProperty>(Expression<Func<TEntity, IEnumerable<TProperty>>> accessor, TProperty[] constantValues) 
{ 
    Expression<Func<TProperty, bool>> predicate = v => constantValues.Contains(v); 
    var body = Expression.Call(typeof(Enumerable), "Any", 
     new[] { typeof(TProperty) }, accessor.Body, predicate); 
    return Expression.Lambda<Func<TEntity, bool>>(body, accessor.Parameters); 
} 
0

Есть ли какой-либо конкретной причины, почему вы нужно генерировать эти выражения динамически, а не просто предоставлять одну функцию в вашем DAL для каждого потенциального запроса?

Ваше описание похоже на то, что пользователь просто предоставляет входные значения для запросов, но не интегральную структуру самого запроса, и в этом случае я бы не слишком усложнял вещи. Ваша модель также может передавать IQueryable и изменять по мере необходимости на основе выбора пользователей (добавьте произвольное количество ограничений, информацию о заказе и т. Д. И т. Д.) Вместо фактического создания выражений.

Если вам действительно нужно идти способом динамического выражения, можете ли вы задать свой вопрос больше? Я не уверен, что это вопрос. Конечно, вы могли бы сами создавать выражения. Имхо, это мир боли, хотя, создавая нетривиальные выражения от руки, это не особенно забавно. Поэтому, если есть другой способ, я обычно рекомендую использовать другой способ.

0

Я также не уверен, как вы намерены динамически определять эти свойства, но то, как вы это делаете, похоже, затрудняет его. Я бы просто прибегнул к старой доброй технике. Попробуйте что-то вроде общего parameterReplacer:

public class ParameterReplacer : ExpressionVisitor 
{ 
    private readonly ParameterExpression m_parameter; 
    private readonly Expression m_replacement; 
    public ParameterReplacer(ParameterExpression parameter, Expression replacement) 
    { 
     this.m_parameter = parameter; 
     this.m_replacement = replacement; 
    } 

    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     if (object.ReferenceEquals(node, m_parameter)) 
      return m_replacement; 
     return node; 
    } 

} 

а затем определить метод расширения, который должен заменить параметр в выражении.

public static class ExpressionExtensions 
{ 
    public static Expression<Func<T1, TResult>> FixParam<T1, T2, TResult>(this Expression<Func<T1, T2, TResult>> expression, T2 parameterValue) 
    { 
     var parameterToRemove = expression.Parameters.ElementAt(1); 
     var replacer = new ParameterReplacer(parameterToRemove, Expression.Constant(parameterValue, typeof(T2))); 
     return Expression.Lambda<Func<T1, TResult>>(replacer.Visit(expression.Body), expression.Parameters.Where(p => p != parameterToRemove)); 
    } 
} 

здесь это второй параметр, но вы, конечно, можете легко его изменить. Теперь с этим, я бы определил мои окончательные Предикаты непосредственно с дополнительным заполнителем, например:

Expression<Func<User, int[], bool>> queryDefinition = (user, receiptIds) => user.Receipts.Any(r => receiptIds.Contains(r.Id)); 

, а затем заменить второй параметр, если у вас есть значение,

var ids = new []{3, 6}; 
var finalquery = queryDefinition.FixParam(ids); 

это дает вам преимущество не нужно беспокоиться о построении дерева выражений, и вы полностью контролируете свои предикаты в коде. PS: https://dotnetfiddle.net/Widget/T3oj5U

Смежные вопросы