Given an Expression<Func<TEntity, bool>>
along the lines of
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")
)
)
)
I am trying to extract a list property conditions by type, i.e.
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" } ]
So far, I have created an ExpressionVisitor
and identified the VisitBinary
method as the one I want to plug into in order to obtain my information.
I am still at a loss about
BinaryExpression
I am looking at represents a terminal statement (in the sense that there are no more nested expressions that I need to look at)BinaryExpression
is concerned withExpressionVisitor
methods to cover for cases that I have not considered yet.Not sure what really is the use case, but here is some starting point
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);
}
}
The idea is simple. Override Visit
method just to maintain a stack of processing expressions. The main processing is inside the VisitMember
override, which is called for each property/field accessor. The node.Expression.NodeType != ExpressionType.Constant
is used to eliminate the closure members, while the second condition eliminates collection properties. Finally, the potential condition expression is extracted from the stack.
The result is including both MemberExpression
and the Expression
where it is used. MemberExpression.Expression.Type
is your entity type, MemberExpression.Member
is the property/field of that type.
Sample test:
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With