Using C# Expression Trees in the Real World
In this article, I will discuss ExpressionTree.
Expression tree is a compelling feature of the C#. It allows us to
- Generate code at runtime
- Rewrite code at runtime
- Translate code at runtime.
Let’s understand the difference is between Func<T>
and Expression<Func<T>>.
with code example. Then we will write some real-world code examples with the help of the expression tree.
Lambda expression as executable code.
Func<int, bool> deleg = i => i < 5;
// Invoke the delegate and display the output.
Console.WriteLine("deleg(4) = {0}", deleg(4));
Lambda expression as data in the form of an expression tree.
Expression<Func<int, bool>> expr = i => i < 5;
// Compile the expression tree into executable code.
Func<int, bool> deleg2 = expr.Compile();
// Invoke the method and print the output.
Console.WriteLine("deleg2(4) = {0}", deleg2(4));
I hope you have understood the difference between Func
and Expression<Func>
with the above two examples. Func is executing code while Expression
is data in the form of the expression tree.
Let’s understand this with a real-world example. Let’s suppose you want to develop an API that takes table name and columns and generate SQL
select statement.
void Main()
{
string tableName = "Customer";
var output=GenerateSql(tableName, "name", "age");
Console.WriteLine(output);
}
public string GenerateSql(string tableName,
params string[] columns)
{
var selectedFields = string.Join(',', columns);
return $"SELECT {selectedFields} FROM {tableName}";
}
If you run the application, you will see the following output
SELECT name, age FROM Customer
Nothing wrong with this code, but this code is very error-prone because there is no check on the column. For example, if the developer mistypes the name as fname,
the program will not complain, and it will give you the error or wrong result.
var output=GenerateSql(tableName, "fname", "age");
Better Way
Let’s write the code in a better way.
void Main()
{
string tableName = "Customer";
var output2=GenerateSql<Customer>(tableName,x=>x.Name,x=>x.Age,x=>x.Phone);
Console.WriteLine(output2);
}
public string GenerateSql<T>(string tableName, params Expression<Func<T, object>>[] columnsName)
{
var fields = new List<string>();
foreach (var selector in columnsName)
{
var body = selector.Body;
if (body is MemberExpression me)
{
fields.Add(me.Member.Name.ToLower());
}
else if (body is UnaryExpression ue)
{
fields.Add(((MemberExpression)ue.Operand).Member.Name.ToLower());
}
}
var selectedFields = string.Join(',', fields);
return $"SELECT {selectedFields} FROM {tableName}";
}
public class Customer
{
public string Name { get; set; }
public int Age { get; set; }
public int Phone { get; set; }
}
The core of the logic is this expression Expression<Func<T, object>>[] columnsName
this expression will provide the data to the C# compiler.
As per the Microsoft
Expression trees represent code in a tree-like data structure. For example, each node is an expression, a method call, or a binary operation such as `x < y’.
You can compile and run code represented by expression trees. This enables dynamic modification of executable code, the execution of LINQ queries in various databases, and the creation of dynamic queries. For more information about expression trees in LINQ
See the Expression tree for the following code.
x=>x.Name
var x = new ParameterExpression {
Type = typeof(Customer),
IsByRef = false,
Name = "x"
};
new Expression<Func<Customer, object>> {
NodeType = ExpressionType.Lambda,
Type = typeof(Func<Customer, object>),
Parameters = new ReadOnlyCollection<ParameterExpression> {
x
},
Body = new UnaryExpression {
NodeType = ExpressionType.Convert,
Type = typeof(object),
Operand = new MemberExpression {
Type = typeof(int),
Expression = x,
Member = typeof(Customer).GetProperty("Age")
}
},
ReturnType = typeof(object)
}