8

给定一个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关心的实体类型
  • 我是否需要覆盖任何其他ExpressionVisitor方法来涵盖我尚未考虑的情况。
4

1 回答 1

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以及Expression使用它的位置。MemberExpression.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;
    }
}
于 2016-01-28T11:07:33.357 回答