Как использовать Expression (Of TDelegate). Метод обновления - PullRequest
5 голосов
/ 24 ноября 2011

Я построил хранилище, используя лямбда-выражения для фильтрации моих коллекций сущностей.В качестве параметра метода я посылаю Expression<Func<Case, bool>> exp.Но внутри метода я хотел бы обновить это же выражение некоторыми глобальными фильтрами.Я вижу, что сам объект выражения получил метод Update, но я не могу понять, как он реализован (и ничего не может найти при поиске в сети).

exp.Update(exp.Body, ???);

Может ли кто-нибудь привести пример??

EDIT: определение метода: http://msdn.microsoft.com/en-us/library/ee378255.aspx

EDIT2: Это мой код (где я пытаюсь использовать .And):

Expression<Func<Case, bool>> newExp = c => c.CaseStatusId != (int)CaseStatus.Finished
var binExp = Expression.And(exp.Body, newExp.Body);
ParameterExpression paramExp = Expression.Parameter(typeof(Expression<Func<Case, bool>>), "c");
return repository.Where(Expression.Lambda<Expression<Func<Case, bool>>>(binExp, 
    new[] { paramExp }).Compile()).ToArray();

Не удаетсясо следующим ArgumentException: параметр типа лямбда должен быть получен из System.Delegate

Ответы [ 2 ]

9 голосов
/ 17 октября 2012

Я не думаю, что метод Update может помочь вам здесь. Он только создает новую лямбду, но не обновляет исходные параметры новыми, вы должны сделать это вручную. Я бы порекомендовал иметь посетителя, который заменит параметр, тогда вы можете And выражения вместе.

Всего вы получите что-то вроде:

    private Case[] getItems(Expression<Func<Case, bool>> exp)
    {
        return repository.Where(AddGlobalFilters(exp).Compile()).ToArray();
    }

    private Expression<Func<Case, bool>> AddGlobalFilters(Expression<Func<Case, bool>> exp)
    {
        // get the global filter
        Expression<Func<Case, bool>> newExp = c => c.CaseStatusId != (int)CaseStatus.Finished;

        // get the visitor
        var visitor = new ParameterUpdateVisitor(newExp.Parameters.First(), exp.Parameters.First());
        // replace the parameter in the expression just created
        newExp = visitor.Visit(newExp) as Expression<Func<Case, bool>>;

        // now you can and together the two expressions
        var binExp = Expression.And(exp.Body, newExp.Body);
        // and return a new lambda, that will do what you want. NOTE that the binExp has reference only to te newExp.Parameters[0] (there is only 1) parameter, and no other
        return Expression.Lambda<Func<Case, bool>>(binExp, newExp.Parameters);
    }


    /// <summary>
    /// updates the parameter in the expression
    /// </summary>
    class ParameterUpdateVisitor : ExpressionVisitor
    {
        private ParameterExpression _oldParameter;
        private ParameterExpression _newParameter;

        public ParameterUpdateVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
        {
            _oldParameter = oldParameter;
            _newParameter = newParameter;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (object.ReferenceEquals(node, _oldParameter))
                return _newParameter;

            return base.VisitParameter(node);
        }
    }
0 голосов
/ 24 ноября 2011
System.Linq.Expressions.Expression.And(exp.Body, newExpression.Body);

Пример:

Expression<Func<int, bool>> f = p => true;
var a = Expression.And(f.Body, f.Body);
ParameterExpression pParam = Expression.Parameter(typeof(int), "p"); 
var b = (new int[] { 1, 2, 3 }).Where(Expression.Lambda<Func<int, bool>>(a,
        new ParameterExpression[] { pParam }).Compile());
...