如何使用表达式树创建动态条件

时间:2012-03-13 07:20:27

标签: c# linq entity-framework c#-4.0 expression-trees

请考虑以下代码:

System.Linq.Expressions.Expression<Func<tbl, bool>> exp_details = r => r.ID_Master == Id &&
r.Year == Year &&
r.Month == Month ;

我想写一个期望一些参数的函数,然后从我的数据库中检索一些数据。问题是我想创建动态条件。例如,如果我将IsDeleted参数传递给true,我想将r.IsDeleted == true;添加到exp_details。我怎么能这样做?

2 个答案:

答案 0 :(得分:2)

执行此操作的关键是使用遍历给定表达式的ExpressionVisitor和(可选地,在子类中)允许将已识别的元素替换为您指定的其他元素。在我的情况下,这是为ORM(NHibernate)完成的。这是我使用的:(我稍后会添加对我的回答的引用)

public class ParameterAssignAndReplacer : ExpressionVisitor
{
    private readonly ParameterExpression _source;
    private readonly ConstantExpression _target;

    internal ParameterAssignAndReplacer(ParameterExpression source, ConstantExpression target)
    {
        _source = source;
        _target = target;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return node.Name == _source.Name ?
            base.VisitConstant(_target) :
            base.VisitParameter(node);
    }
}

和..

public static class ExpressionExtensions
{
    public static Expression<Func<TArg2, TResult>> AssignAndReduce<TArg1, TArg2, TResult>(
        this Expression<Func<TArg1, TArg2, TResult>> func,
        TArg1 parameter)
    {
        var exprBody = func.Body;
        var assignedParameter = Expression.Constant(parameter, typeof(TArg1));
        exprBody = new ParameterAssignAndReplacer(func.Parameters[0], assignedParameter).Visit(exprBody);
        return Expression.Lambda<Func<TArg2, TResult>>(exprBody, func.Parameters[1]);
    }
}

这两个类都可以扩展到您的特定场景。为了使这与您发布的代码相关(为简单起见,我仅使用IsDeleted作为参数):

public class SomeClass
{
    Expression<Func<tbl, bool, bool>> _templateExpression =
       (tbl r, bool isDeleted) => r.ID_Master == 5 && r.Year == 2008 && r.Month == 12 && r.IsDeleted == isDeleted;

    public Expression<Func<tbl, bool>> Foo(bool IsDeleted)
    {
        return _templateExpression.AssignAndReduce(IsDeleted);
    }
}

关于参考文献,我在这个主题上学到的大部分内容来自Marc Gravell's "Expressions" answers [虽然很多其他用户帮助我掌握了这个:-)]

答案 1 :(得分:1)

你不能将语句体用于Linq Expression 考虑使用Predicate而不是

        var exp_details = new Predicate<tbl>(r =>
        {
            bool result == Id && r.Year == Year && r.Month == Month;
            if(IsDeleted != null)
            {
                result &= r.IsDeleted == IsDeleted;
            }
            return result;
        });

最多Linq Func&lt; T,bool&gt; 表达式可以替换为 Predicate&lt; T&gt;

使用“简单”linq表达式生成动态WHERE原因。

IQueryable<tbl> query = ent.tbl.Where(r => r.ID_Master == Id && r.Year == Year);
//customize query
if(IsDeleted != null){
  query = query.Where(r => r.IsDeleted == IsDeleted);
}
//execute the final generated query
var result = query.FirstOrDefault();

这将从IQueryable<T>创建原因。 Linq足够聪明,可以进行复杂的查询。