【问题标题】:Expression parameter is not defined表达式参数未定义
【发布时间】:2018-06-21 09:02:04
【问题描述】:

我正在尝试对列表执行查询,以使用在代码中其他位置设置的表达式提供即时结果,而第二个线程关闭并使用它从 Linq 中的数据库获取完整的结果集查询。

我知道表达式本身没问题,因为当我通过线路将它发送到服务器端并将其应用于IQueryable 时,它就会起作用。但是,在客户端应用时会产生以下错误:

System.InvalidOperationException:从范围“”引用的“MissionControlSuite.Shared.Interface.Model.IBox”类型的“变量”项,但未定义”

执行过滤的代码:

public abstract class GridWithPaging<T> where T : class
{
    List<T> _items

    public Expression<Func<T, bool>> Filter { get; set; }
    public ObservableCollection<IGridFilter<T>> Filters { get; set; }

    // more code skipped for breveity

    public void FiltersChanged(object sender, EventArgs e)
    {
        Expression<Func<T, bool>> combined = item => true;
        foreach (var filter in Filters)
            combined = Expression.Lambda<Func<T, bool>>(
                Expression.AndAlso(filter.Expression.Body, combined.Body),
                combined.Parameters.Single());

        Filter = combined;
        NotifyPropertyChanged("Filter");
    }

    public void AddFilter(IGridFilter<T> filter)
    {
        Filters.Add(filter);
        NotifyPropertyChanged("Filters");
    }

    private void DoFiltering(int start)
    {
        var newView = _items.Skip(start);
        if(Filter != null)
            newView = newView.AsQueryable().Where(Filter);

        //some more code that acts on the filtered list
    }
}

表达式在其他地方设置如下:

Expression<Func<IBox, bool>> expression = item => item.BoxId.Contains(v);

var filter = new GridFilter<IBox>()
{
    Name = string.Format("{0} contains {1}", Property, Value),
    Expression = expression
};

Grid.AddFilter(filter);

【问题讨论】:

  • FiltersChanged 方法需要从 sender 参数中获取数据,而不是从 Filters 中获取数据。

标签: c# linq expression expression-trees linq-expressions


【解决方案1】:

这个答案会对你有所帮助:Combining two expressions (Expression<Func<T, bool>>)

总结一下:问题在于参数虽然同名,但与ParameterExpressioninstance不同——comment to that answer中包含的参数。

解决方案是使用访客(从链接帖子复制的代码):

public static Expression<Func<T, bool>> AndAlso<T>(
    this Expression<Func<T, bool>> expr1,
    Expression<Func<T, bool>> expr2)
{
    var parameter = Expression.Parameter(typeof (T));

    var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
    var left = leftVisitor.Visit(expr1.Body);

    var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
    var right = rightVisitor.Visit(expr2.Body);

    return Expression.Lambda<Func<T, bool>>(
        Expression.AndAlso(left, right), parameter);
}



private class ReplaceExpressionVisitor
    : ExpressionVisitor
{
    private readonly Expression _oldValue;
    private readonly Expression _newValue;

    public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
    {
        _oldValue = oldValue;
        _newValue = newValue;
    }

    public override Expression Visit(Expression node)
    {
        if (node == _oldValue)
            return _newValue;
        return base.Visit(node);
    }
}

另请注意:ORM 提供程序(或您使用的另一个 IQueryable)可能有效,因为它直接将其转换为文本,而执行 (Invokeing) 这个LambdaExpression 会导致错误,因为它在某种程度上更严格。一些IQueryables 也可以接受简单的string 参数并自行解析,这应该表明它们具有更广泛的可接受输入范围。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-24
    • 2013-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-31
    • 2018-07-30
    相关资源
    最近更新 更多