I have the following class Person
, with a custom Where
method:
public class Person
{
public string Name { get; set; }
publi
The problem is that the Expression is actually a tree.
For example, you have the following predicate:
Expression<Func<Person, bool>> expr = x => x.Name == "Shlomi" && x.Age == 26;
If you inspect expr, you'll see that it's body has "AndAlso" NodeType
and it also has two properties for operands -- Left
and Right
each pointing to another Expression
object with "Equal" NodeType
which in turn again have two properties Left
and Right
which point to Expression
of type MemberAccess
and Constant
respectively.
While you can process that tree and extract all information you need, you'll end up implementing your own LINQ2SQL provider, e.g. reinventing a wheel. If you feel so, I hope I provided enough information to start digging...
What you're wanting to do is very complicated, and there are entire frameworks that are built to do this, so you don't have to write the logic yourself. Take a look at LINQ to Entities and LINQ to SQL, e.g.
I certainly think you should follow StriplingWarrior's advice and use LINQ to Entities or LINQ to SQL, but for the sake of reinventing the wheel (poorly) I'll build off of a prior answer of mine.
// Start with a method that takes a predicate and retrieves the property names
static IEnumerable<string> GetColumnNames<T>(Expression<Func<T,bool>> predicate)
{
// Use Expression.Body to gather the necessary details
var members = GetMemberExpressions(predicate.Body);
if (!members.Any())
{
throw new ArgumentException(
"Not reducible to a Member Access",
"predicate");
}
return members.Select(m => m.Member.Name);
}
Now you need to walk the expression tree, visiting each candidate expression, and determine if it includes a MemberExpression
. The GetMemberExpressions
method below will walk an expression tree and retrieve each of the MemberExpression
s found within:
static IEnumerable<MemberExpression> GetMemberExpressions(Expression body)
{
// A Queue preserves left to right reading order of expressions in the tree
var candidates = new Queue<Expression>(new[] { body });
while (candidates.Count > 0)
{
var expr = candidates.Dequeue();
if (expr is MemberExpression)
{
yield return ((MemberExpression)expr);
}
else if (expr is UnaryExpression)
{
candidates.Enqueue(((UnaryExpression)expr).Operand);
}
else if (expr is BinaryExpression)
{
var binary = expr as BinaryExpression;
candidates.Enqueue(binary.Left);
candidates.Enqueue(binary.Right);
}
else if (expr is MethodCallExpression)
{
var method = expr as MethodCallExpression;
foreach (var argument in method.Arguments)
{
candidates.Enqueue(argument);
}
}
else if (expr is LambdaExpression)
{
candidates.Enqueue(((LambdaExpression)expr).Body);
}
}
}