Denis Jakus

Demystifying Tech & Management

CTO x Unique People

AI / ML / ChatGPT

Unreal Engine

Low Code

Management & Leadership

IT Consultant

Denis Jakus

Demystifying Tech & Management

CTO x Unique People

AI / ML / ChatGPT

Unreal Engine

Low Code

Management & Leadership

IT Consultant

CTO Note

EXPRESSION TREES – What, When and How ?3 min read

19 September 2019 .NET
EXPRESSION TREES – What, When and How ?3 min read

Hi there!

Due to the lack of time and a bunch of projects I’m working on in parallel, I’ve decided to publish every two weeks a new post.

In my last post I was writing about generic filter in which I used expression trees.

So let’s clarify what expression tree is!?

What is it all about?

It’s all about data which represents our code in a tree like structure.

Expression tree ( Expression<>) is a not-compiled data which represents our code. It’s a blueprint of what we want to achieve using LINQ.

So what the compiler does is, it takes Expression type(which is btw strongly typed lambda expression) and compiles it into Expression tree instead of executable code.

This way we can build our own code and later execute it when we want to!

When should we use it?

Good example of usage is described in my previous post, creating generic filter.

Since we want to build our filter based on dynamic number of properties, we should firstly create our filter and then compile it and execute it.

We can easily break down our code in multiple LINQ queries and glue it all together to get our final result.

Building blocks of Expression Tree

Expression tree consists of:

  1. Parameters
  2. Body
  3. NodeType
    1. Lambda
    2. Constant
    3. Parameter
    4. Equal
    5. AndAlso
    6. GreaterThan
    7. MemberAccess
    8. … (complete list is here: https://docs.microsoft.com/en-us/dotnet/api/system.linq.expressions.expression.accept?view=netframework-4.8)
  4. Return Type

Example

So, to build an expression tree we need to:

  1. pass parameters
  2. create a body expression
  3. create nodetype and pass body expression with parameters
  4. define return type
  5. compile() our expression tree
  6. execute

Simplified example

// 1. create parameters
ParameterExpression param1 = Expression.Parameter(typeof(int), "param1");
ParameterExpression param2 = Expression.Parameter(typeof(int), "param2");
// 1. create parameters
ParameterExpression[] parameters = new ParameterExpression[] { param1, param2 };
// 2. create body expression
BinaryExpression bodyExpression = Expression.Subtract(param1, param2);
// 3. create the nodetype and pass body expression 
Expression<Func<int, int, int>> expression = Expression.Lambda<Func<int, int, int>>(bodyExpression, parameters);
// 4. defining return type and 5. compile the expression
Func<int, int, int> compiledExpression = expression.Compile();
// 6. execute  
int result = compiledExpression(10, 3); //returns 7

Final thoughts

The example above is simplified, so you could get the clear picture how does it work. Expression trees can get a bit more complex but this is the way they work.

In case you read my previous article, you might have a question what ExpressionVisitor is!?

To learn more, please read ahead.

P.S. ExpressionVisitor

What’s that?

It’s a special class used inside Expression trees to modify tree structure, based on Visitor pattern.

Why should I try to modify immutable tree structure?

In some cases you want to operate with different types inside .Where() clause. To achieve this you will need to use ExpressionVisitor to make some modifications.

I.e. no.1

If your database has some fields containing (0,1) and you want to operate with flag which is of type bool. You will implement ExpressionVisitor to change property and be able to use it inside .Where() clause.

I.e. no.2

Switching from one database to another MS SQL -> SQLite. Some things are different inside database and if you don’t want to change your DAL, you just add some ExpressionVisitor and do the modifications you need to completely switch to SQLite.

Related Posts
Write a comment