2014-09-08 2 views
0

Я пытаюсь создать динамический AndAlso фильтр, который будет использоваться в методе Where к LINQ-to-EF запросу:Параметр Замена, когда параметр является сложным объектом

query.Where(filterExpression) 

где filterExpression представляет собой скомпилированная лямбда

До сих пор я реализовал цикл с некоторым причудливым использованием, чтобы проверять случаи, когда есть только один, два или более запросов.

Expression selectLeft = null; 
Expression selectRight = null; 
Expression filterExpression = null; 

foreach (QueryColumn columnQuery in querycolumns) 
{ 
    Expression<Func<FileColumnRecords, bool>> 
       columnPredicate = d => d.fcv.Any(f => (f.value != null ? 
            f.value.ToLower().Contains(columnQuery.queryTerm.ToLower()) : 
            false)); 

    if (selectLeft == null) 
    { 
     selectLeft = columnPredicate.Body; 
     filterExpression = selectLeft; 
     continue; 
    } 
    if (selectRight == null) 
    { 
     selectRight = columnPredicate.Body; 
     filterExpression = 
     Expression.AndAlso(selectLeft, selectRight); 
     continue; 
    } 

    filterExpression = 
      Expression.AndAlso(filterExpression, columnPredicate.Body); 
} 

Затем я создал ParameterReplacer, чтобы гарантировать, что все итерации моего параметра выражения получить ту же ссылку:

ParameterExpression param = Expression.Parameter(typeof(FileColumnRecords), "p"); 
ParameterReplacer replacer = new ParameterReplacer(param); 
filterExpression = replacer.Visit(filterExpression); 

, который построен из:

class ParameterReplacer : ExpressionVisitor 
{ 
    private readonly ParameterExpression parameter; 

    internal ParameterReplacer(ParameterExpression parameter) 
    { 
     this.parameter = parameter; 
    } 

    protected override Expression VisitParameter 
     (ParameterExpression node) 
    { 
     return parameter; 
    } 
} 

(ParameterReplacer класс любезно JonSkeet)

При запуске t он ParameterReplacer я получаю следующее сообщение об ошибке:

"Property 'System.String value' is not defined for type 'Models.FileColumnRecords'" 

где FileColumnRecords определяется как:

public class FileColumnRecords 
{  
    public Documents doc; 

    public IEnumerable<FileColumnValues> fcv; 
} 

и FileColumnValues как:

public partial class FileColumnValues 
{ 
    public long ID { get; set; } 
    public long CNID { get; set; } 
    public long fileID { get; set; } 
    public string value { get; set; } 
} 

ответ

3

Вы бежите в трудности из-за там быть параметрами Я подозреваю, что он встроен в ваше дерево выражений. Заменитель должен заменять только параметры верхнего уровня.

Вы могли бы передать выражения верхнего уровня в ParameterReplacer:

class ParameterReplacer : ExpressionVisitor 
{ 
    private readonly ParameterExpression target; 
    private readonly ISet<ParameterExpression> sources; 

    internal ParameterReplacer(ParameterExpression target, 
           IEnumerable<LambdaExpression> expressions) 
    { 
     this.target = target; 
     sources = new HashSet<ParameterExpression>(
      expressions.SelectMany(e => e.Parameters)); 
    } 

    protected override Expression VisitParameter 
     (ParameterExpression node) 
    { 
     return sources.Contains(node) ? target : node; 
    } 
} 

Вы бы тогда нужно изменить остальную часть кода, чтобы создать список деревьев выражений вы были сочетающих. Есть определенно более элегантные способы сделать это, но я думаю, что это должно сработать. Это как раз у меня на голове, но я не пробовал.

Сказав все это, вы уверены, что не можете просто использовать PredicateBuilder?

+0

Спасибо, что ответили, Джон! 'PredicateBuilder' выглядит действительно интересным. Вы думаете, что можно передать весь мой «columnPredicate» в «PredicateBuilder» или мне нужно будет построить дерево выражений, как вы предлагаете в своем ответе? – seebiscuit

+0

@ Пояснение: Я не совсем понимаю, что вы делаете, но я подозреваю, что вы можете передать весь «columnPredicate». –

+0

Это определенно стоит попробовать. Позвольте мне взять это на тест-драйв. – seebiscuit

0

на основе JonSkeet-х Answer на этот пост, используя удивительный метод PredicateBuilder JonSkeet предложенный из LINQKit библиотеки я придумал следующее простое решение:

public static IQueryable<FileColumnRecords> DynamicColumnQuery 
           (this IQueryable<FileColumnRecords> query, 
           List<QueryColumn> querycolumns) 
{ 
    var predicate = PredicateBuilder.False<FileColumnRecords>(); 
    foreach (QueryColumn columnQuery in querycolumns) 
    { 
     string temp = columnQuery.queryTerm.ToLower(); 
     predicate = predicate.Or(d => d.fcv.Any(f => (f.value != null ? 
           f.value.ToLower().Contains(temp) : false)); 
    } 

    return query.AsExpandable().Where(predicate); 
} 

Если вы знаете немного о написании деревьев выражений, то вам 'сразу же, чтобы оценить, как невероятно AWESOME это. Плюс, обратите внимание, как у меня встроенные параметры в моем предикате.

I am in no way affiliated with the developers of LINQKit nor Joseph or Ben Albahari nor O'Reilly publisher. Just an incredibly satisfied developer who thought I should share the awesomeness of what they built.

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