I have the following class Person
, with a custom Where
method:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Where(Expression<Func<Person, bool>> predicate)
{
return String.Empty;
}
}
How can I retrieve the Expression
parameter names and values in an enumerable manner?
Person p = new Person();
p.Where(i => i.Name == "Shlomi" && i.Age == 26);
For the purpose of building a string query with parameters attached according to the name and value of the expression.
// Eventually I will convert the above into the following:
string Query = "select * from person where name = @Name AND Age = @Age";
SqlParameter[] param = new SqlParameter[] {
new SqlParameter("@Name","Shlomi"),
new SqlParameter("@Age","26")
};
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);
}
}
}
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.
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