This post is a sequence for the previous article which shows why the Func delegate is not translated into the SQL statements.
Let’s investigate why our func delegate is not translated as we have expected in the previous article.
We can see the query that will be translated for the queryables by writing out the IQueryable
to the console.
Here it is
Expression<Func<Customer, bool>> exprPred = p => p.FirstName == "Design Engineer"; var names = context.Customers.Where(exprPred); Console.Write("Query : " + names); /*Query : SELECT [Extent1].[CustomerID] AS [CustomerID], [Extent1].[NameStyle] AS [NameStyle], [Extent1].[Title] AS [Title], [Extent1].[FirstName] AS [FirstName], [Extent1].[MiddleName] AS [MiddleName], [Extent1].[LastName] AS [LastName], [Extent1].[Suffix] AS [Suffix], [Extent1].[CompanyName] AS [CompanyName], [Extent1].[SalesPerson] AS [SalesPerson], [Extent1].[EmailAddress] AS [EmailAddress], [Extent1].[Phone] AS [Phone], [Extent1].[PasswordHash] AS [PasswordHash], [Extent1].[PasswordSalt] AS [PasswordSalt], [Extent1].[rowguid] AS [rowguid], [Extent1].[ModifiedDate] AS [ModifiedDate] FROM [SalesLT].[Customer] AS [Extent1] WHERE N'Design Engineer' = [Extent1].[FirstName] */
We got the SQL query for the IQueryable
here and it is what it was expected.
Now, let’s try the same thing for our Func delegate and see what it will output when we write it to console.
Func<Customer, bool> funcPred = p => p.FirstName == "Design Engineer"; var names = context.Customers.Where(funcPred); Console.Write("Query : " + names); //Query : System.Linq.Enumerable+WhereEnumerableIterator`1[QueryProviderImpl.Customer]
The output is different here. This is making a call to WhereEnumerableIterator
in Enumerable class.
Let’s look at the .Where()
method source code and WhereEnumerableIterator
class from the Microsoft source.
public static partial class Enumerable { public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { if (source == null) throw Error.ArgumentNull("source"); if (predicate == null) throw Error.ArgumentNull("predicate"); if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate); if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate); if (source is List<TSource>) return new WhereListIterator<TSource>((List<TSource>)source, predicate); return new WhereEnumerableIterator<TSource>(source, predicate); } //... some other methods go here }
As said in the previous post, there are overloads for .Where
method. One for the Expression and the other for the Func. Now the above one called as we are passing a Func to the second parameter.
The .Where()
Extension method on the Enumerable class is returning an instance of WhereEnumerableIterator
class.
Here is the implementation of WhereEnumerableIterator class.
// WhereEnumerableIterator.cs class WhereEnumerableIterator<TSource> : Iterator<TSource> { IEnumerable<TSource> source; Func<TSource, bool> predicate; IEnumerator<TSource> enumerator; public WhereEnumerableIterator(IEnumerable<TSource> source, Func<TSource, bool> predicate) { this.source = source; this.predicate = predicate; } public override Iterator<TSource> Clone() { return new WhereEnumerableIterator<TSource>(source, predicate); } public override void Dispose() { if (enumerator is IDisposable) ((IDisposable)enumerator).Dispose(); enumerator = null; base.Dispose(); } public override bool MoveNext() { switch (state) { case 1: enumerator = source.GetEnumerator(); state = 2; goto case 2; case 2: while (enumerator.MoveNext()) { TSource item = enumerator.Current; if (predicate(item)) { current = item; return true; } } Dispose(); break; } return false; } public override IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector) { return new WhereSelectEnumerableIterator<TSource, TResult>(source, predicate, selector); } public override IEnumerable<TSource> Where(Func<TSource, bool> predicate) { return new WhereEnumerableIterator<TSource>(source, CombinePredicates(this.predicate, predicate)); } }
The WhereEnumerableIterator class will implement the Iterator class. The Iterator
is an abstract class which implements IEnumerable
and IEnumerator
interfaces except the MoveNext()
method. So, our WhereEnumerableIterator class will implement the iterator pattern and has the MoveNext()
method implemented.
Let’s come back to our query on the Customers entity
Func<Customer, bool> funcPred = p => p.FirstName == "Design Engineer"; var names = context.Customers.Where(funcPredicate);
Let me pull the MoveNext()
method of WhereEnumerableIterator
class so that it will be clear.
public override bool MoveNext() { switch (state) { case 1: enumerator = source.GetEnumerator(); state = 2; goto case 2; case 2: while (enumerator.MoveNext()) { TSource item = enumerator.Current; if (predicate(item)) { current = item; return true; } } Dispose(); break; } return false; }
As we iterate the items in the names
in a foreach or when .ToList() is called on the names, every item from the customer table is pulled from the database with the enumerator.Current and compared against the predicate(the func delegate in our case) that we pass in.
The difference in query execution is fairly large because expression trees and func delegates on the entities take a different approach in execution.
If we pass an expression tree to .Where()
method then the execution will be different as the query is translated into an appropriate query based on the query provider (SQL statements for SQL Provider).
If we pass a func delegate to .Where()
method then the appropriate records from the database are pulled and compared against the predicate and will return the result.
Entity Framework implements a provider model for different databases such as SQL, Oracle, MySQL, PostgreSQL etc. These query providers will have to translate the IQueryables into SQL as the query for a database will be different for different databases.
The translation for the expression trees will be interesting as it involves providers, translations, expression visitors etc. As this is not a topic for this article I’m not pulling those topics.
If you are interested in finding how the expression tree translation works look at the references section.
Karthik is a passionate Full Stack developer working primarily on .NET Core, microservices, distributed systems, VUE and JavaScript. He also loves NBA basketball so you might find some NBA examples in his posts and he owns this blog.
In this post, we’ll see how to test gRPC Server applications using different clients. And… Read More
In this post, we'll create a new gRPC project in ASP.NET Core and see what's… Read More
In this blog post, we’ll see how to run dotnet core projects without opening visual… Read More
Programmatically evaluating policies is useful when we want to provide access or hide some data… Read More
We saw how we could set up policy-based authorization in our previous article. In this… Read More
What is policy-based authorization and how to set up policy-based authorization with handlers and policies… Read More
This website uses cookies.