如何使用Expression(Of TDelegate).Update方法

6

我使用Lambda表达式构建了一个仓库来过滤我的实体集合。作为该方法的参数,我发送了Expression<Func<Case, bool>> exp。但是,在方法内部,我希望使用一些全局过滤器更新同一表达式。我可以看到表达式对象本身有一个Update方法,但我无法弄清它是如何实现的(在搜索网络时找不到任何信息)。

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

有人能给一个例子吗?

编辑:方法的定义:http://msdn.microsoft.com/en-us/library/ee378255.aspx

编辑2:这是我的代码(我尝试使用.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();

它会出现以下参数异常:Lambda类型参数必须派生自System.Delegate。

注意:Expression(Of TDelegate)是VB语法。在文档页面的顶部,您可以切换到C#。 - Olivier Jacot-Descombes
2个回答

9
我认为Update方法在这里无法帮助你。它只会创建一个新的lambda,但不会使用新的lambda更新原始参数,你必须手动更新。 我建议创建一个visitor来替换参数,然后你可以使用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);
        }
    }

非常抱歉没有“关闭”这个话题。通过更改业务逻辑,问题已经得到解决... - baddaydaddy

0
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());

你能再解释一下或者给一个更具体的例子吗?.And() 方法返回一个 BinaryExpression 对象,而我需要一个 Expression<Func<Case, bool>> 对象。 - baddaydaddy
当我尝试将此代码实现到我的程序中时,出现了“二进制运算符Add未定义类型'System.Boolean'和'System.Boolean'”的错误。 - baddaydaddy
这是偏离了方向,而且我本应该使用(昨天有点累)。请参阅我的原始帖子以获取有关And方法测试的详细信息... - baddaydaddy
用Expression.Lambda<Func<Case, bool>>替换Expression.Lambda<Expression<Func<Case, bool>>>。 - Reza ArabQaeni
这会引发异常:“参数类型:'System.Func<Case, bool>' 无法分配给系统类型 'System.Linq.Expressions.Expression<System.Func<Case, bool>>'”。 - baddaydaddy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接