【问题标题】:Extract all conditions from Expression by Type按类型从表达式中提取所有条件
【发布时间】:2016-01-27 16:24:40
【问题描述】:

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

【问题讨论】:

  • 你能贴出ExpressionVisitor的代码吗?

标签: c# lambda expression expression-trees expressionvisitor


【解决方案1】:

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

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;
    }
}

【讨论】:

  • 这是一个非常有趣的想法!!!不幸的是,有太多的极端情况,你只会在途中发现,作为错误。例如 t.BoolProp == true && t.BoolProp 表示相同,但​​表示方式完全不同。 t.CollectionProp.Any(), t.CollectionProp.FirstOrDefault() != null 等等等等等等……这个问题很快就会变得非常复杂!!!我很确定在某些情况下甚至不清楚如何进行。但是为堆栈 +1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-14
  • 1970-01-01
  • 2023-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多