从Expression by Type中提取所有条件

时间:2016-01-27 16:24:41

标签: c# lambda expression expression-trees expressionvisitor

给出{/ 1}}

Expression<Func<TEntity, bool>>

我试图按类型提取列表属性条件,即

entity => entity.SubEntity.Any(
    subEntity => (
        (subEntity.SomeProperty == False)
        AndAlso
        subEntity.SubSubEntity.FooProperty.StartsWith(
            value(SomeClass+<>c__DisplayClass0).ComparisonProperty
        )
        AndAlso
        subEntity.SubSubEntity.BarProperty == "Bar"
        AndAlso
        subEntity.SubSubEntity.SubSubSubEntity.Any(
            subSubSubEntity => (x.SubSubSubSubEntity.BazProperty == "whatever")
        )
    )
)

到目前为止,我创建了一个TEntity : [ /* no conditions for immediate members of TEntity */ ] TSubEntity : [ { SomeProperty == False } ] TSubSubEntity : [ { FooProperty.StartsWith(/* ... */) }, { BarProperty == "Bar" } ], TSubSubSubEntity : [ /* no conditions for immediate members of TSubSubSubEntity */ ], TSubSubSubSubEntity : [ { BazProperty == "whatever" } ] 并将ExpressionVisitor方法标识为我要插入的方法以获取我的信息。

我仍然感到茫然

  • 如何确定我正在查看的VisitBinary是否代表一个终结语句(从某种意义上说,我不需要查看更多嵌套表达式)
  • 如何确定BinaryExpression关注的实体类型
  • 我是否需要覆盖任何其他BinaryExpression方法,以涵盖我尚未考虑过的案例。

1 个答案:

答案 0 :(得分:4)

不确定用例是什么,但这里有一些起点

class TestVisitor : ExpressionVisitor
{
    public Dictionary<Type, List<Tuple<MemberExpression, Expression>>> Result = new Dictionary<Type, List<Tuple<MemberExpression, Expression>>>();
    Stack<Expression> stack = new Stack<Expression>();
    public override Expression Visit(Expression node)
    {
        stack.Push(node);
        base.Visit(node);
        stack.Pop();
        return node;
    }
    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Expression.NodeType != ExpressionType.Constant && (node.Type == typeof(string) || !typeof(IEnumerable).IsAssignableFrom(node.Type)))
        {
            var expression = stack.Skip(1).FirstOrDefault();
            if (expression != null && expression.Type == typeof(bool))
            {
                List<Tuple<MemberExpression, Expression>> resultList;
                if (!Result.TryGetValue(node.Expression.Type, out resultList))
                    Result.Add(node.Expression.Type, resultList = new List<Tuple<MemberExpression, Expression>>());
                resultList.Add(Tuple.Create(node, expression));
            }
        }
        return base.VisitMember(node);
    }
}

这个想法很简单。覆盖Visit方法只是为了维护一堆处理表达式。主要处理在VisitMember覆盖内,为每个属性/字段访问者调用。 node.Expression.NodeType != ExpressionType.Constant用于消除闭包成员,而第二个条件用于消除集合属性。最后,从堆栈中提取潜在条件表达式。

结果包括MemberExpression和使用它的ExpressionMemberExpression.Expression.Type是您的实体类型,MemberExpression.Member是该类型的属性/字段。

样品测试:

class Entity
{
    public ICollection<SubEntity> SubEntity { get; set; }
}

class SubEntity
{
    public bool SomeProperty { get; set; }
    public SubSubEntity SubSubEntity { get; set; }
}

class SubSubEntity
{
    public string FooProperty { get; set; }
    public string BarProperty { get; set; }
    public ICollection<SubSubSubEntity> SubSubSubEntity { get; set; }
}

class SubSubSubEntity
{
    public SubSubSubSubEntity SubSubSubSubEntity { get; set; }
}

class SubSubSubSubEntity
{
    public string BazProperty { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        string comparisonProperty = "Ivan";
        Expression<Func<Entity, bool>> e =
            entity => entity.SubEntity.Any(subEntity =>
                subEntity.SomeProperty == false
                &&
                subEntity.SubSubEntity.FooProperty.StartsWith(comparisonProperty)
                &&
                subEntity.SubSubEntity.BarProperty == "Bar"
                &&
                subEntity.SubSubEntity.SubSubSubEntity.Any(subSubSubEntity => subSubSubEntity.SubSubSubSubEntity.BazProperty == "whatever")
                );
        var v = new TestVisitor();
        v.Visit(e);
        var result = v.Result;
    }
}