Published 11 October 2019

Performance – Expression vs Func

Performance between Expression and Func

As a consumer there is often no difference in syntax when using API:s that accepts either Expression<Func> or Func, the lambda expressions (e.g. x => x.Value > 0) looks the same in both cases. This leads to confusion when to use one over the other - a confusion that can drastically affect performance. This is commonly observed in Repository classes over an ORM like for example EntityFramework.

Comparing the two

A Func<T> is a function, which (optionally) accepts a value and returns a value. It can take the form of a lambda expression but may also take the form of a method body (e.g. x => { return x.Value > 0; }). On the other hand, an Expression<Func<T>> (Or more generically Expression<TDelegate>) is not a function. It is metadata about the function, represented as an expression tree. This allows us to inspect and parse the meaning of the lambda expression. I explicitly write lambda expression because an Expression<Func<T>> does not accept a method body – only lambda expressions. This is fundamental to how, for example, EntityFramework translates our LINQ-statements into SQL statements.

The example below will show you why you should work with Expressions rather than Funcs in your application code when working with for example EntityFramework.

Comparing the performance between Expression and Func

Consider an ASP.NET application using EntityFramework that has the following trivial model

Say we have 1 000 000 rows of this entity in the database and we have two methods that accepts a lambda in our Repository. The first method accepts an Expression of a Func and the second accepts a Func, see example below.

When consuming this repository, the lambda sent into each function is identical, so one can see that it is very easy to confuse the two.

repository.GetByExpression(x => x.Value > 0);
repository.GetByFunc(x => x.Value > 0);

The outcome of each function, though, is very different. Look at these two SQL-statements genereated by EntityFramework.

Func<T>
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Value] AS [Value]
FROM [dbo].[Entities] AS [Extent1]

Time taken: 10972 ms

Expression<Func<T>>
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Foo] AS [Foo],
[Extent1].[Value] AS [Value]
FROM [dbo].[Entities] AS [Extent1]
WHERE [Extent1].[Value] > 0

Time taken: 2238 ms

Conclusion

The difference in time between the two are drastic (~11s vs ~2,2s), this is because when using Expressions, EntityFramework can build a correct and efficient query based on the metadata provided by the Expression-class, however using Funcs makes this impossible. In that case EntityFramework can only build up a Select clause, there is no Where clause. EF simply cannot interprete the intent of the Where-clause and is therefore forced to fetch ALL the data and then perform the filter (Where) in-memory. This is the reason behind the big gap in performance between the two.

As you can see, prefer Expression<T> over Func<T> when working with code that relies on the metadata around the function or performance will suffer.

Andreas Hagsten, System Developer at Infozone