在多个过滤器中使用相同的lambda参数

时间:2015-03-04 21:24:10

标签: c# linq entity-framework parameter-passing expression-trees

我正在编写一个类,允许用户根据搜索词搜索实体,在实体框架下工作。每个可搜索的字段都在课程中注册。对于字符串字段,搜索只会调用String.Contains。对于查找值,需要调用数据库查找表。

例如,这是如何注册一个简单的字符串过滤器:

Searcher<Account> searcher = new Searcher<Account>();
searcher.AddFilterMapping("Name", e => e.AccountName);

这就是注册查找值的样子:

searcher.AddFilterMapping("Type", e => context.AccountTypes.Where(t => t.Id == e.AccountTypeId).Select(t => t.Description).FirstOrDefault());

上面代码中的context是实体框架上下文。现在,理想情况下,有人会像这样指定一个搜索词:

searcher.SearchTerm = searchTerm;

然后搜索应循环遍历过滤器映射(lambda表达式)并更新其lambda表达式以应用String.Contains。我能够编写一些非常简单的代码来添加方法调用。

MethodInfo containsMethod = typeof(String).GetMethod("Contains");
ConstantExpression searchTermExpression = Expression.Constant(searchTerm);
foreach (Expression<Func<TEntity, string>> selector in selectors)
{
    Expression converter = selector.Body;
    MethodCallExpression containsExpression = Expression.Call(converter, containsMethod, searchTermExpression);
    LambdaExpression lambdaExpression = Expression.Lambda<Func<TEntity, bool>>(containsExpression, selector.Parameters);
    // Then apply the filter to the IQueryable<TEntity> via Where
}

如果我正在做的就是一个接一个地应用每个过滤器,这样可以正常工作。但是,我想使用OR而不是AND进行过滤。在这种情况下,我想要一个lambda表达式。我只是使用Expression.Or组合lambda体。

问题是ParameterExpression对于传递给AddFilterMapping的每个表达式树都不同。我得到的错误是&#34;参数&#39; e&#39;没有绑定在LINQ to Entity表达式中。&#34;我很确定这是因为它们是完全不同的参数表达式,即使它们具有相同的名称。

有没有办法确保在我的所有lambda表达式中共享相同的ParameterExpression

1 个答案:

答案 0 :(得分:2)

您可以使用以下方法将一个表达式的所有实例替换为另一个:

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

使用此方法,您可以创建一个用于lambda的参数表达式,然后使用您创建的新参数替换lambda中的所有参数实例。