F
F
footballer2017-02-13 16:52:41
.NET
footballer, 2017-02-13 16:52:41

LINQ in C#: what happens to a Func that is nested in an Expression?

The user has an organization, the organization has users. We use this code:

_dbContext.Organizations.Where(o => o.Name.Contains("Roga") && o.Users.Any(u => u.FirstName == "Petr")).Select(...)

in organizations we are looking for only those organizations whose name contains "Roga" and in users there is at least 1 Peter.
Here we have two lambdas: the outer one, which is Expression, because it is passed to Where, which is called from DbSet, which implements IQueryable. And the inner one, which is Func because it's called on ICollection, it's not an Expression because ICollection doesn't implement IQueryable.
Because the first lambda is an Expression, then the query provider (or whatever parses the query) will be able to parse it and get information about all the "nodes" that are used in the expression: about object fields (o.Name, o.Users) , about variables ("Roga "), about the operators (in this case &&) and about the methods that are called on the fields (the Any and Contains methods, and then the parser, knowing that Any and Contains are called, will already understand what sql commands to convert these methods to). This is how I understand what is happening when using Expression.
But here it annoys me that we are passing a nested lambda to the Any method, which is not an expression. After all, the parser must receive information from within the nested lambda that "we need to take the FirstName field from the User and compare it with "Petr"). But the nested lambda is not an expression, i.e., it will not be possible to parse it? Or despite the fact that the nested lambda is not an Expression, but a Func, but due to the fact that it is inside the expression,
the sysharp will still pass it as an Expression and it can be parsed? "Petr", this was the request:
FROM  [dbo].[Organizations] AS [Extent1]
    WHERE ([Extent1].[Name] LIKE N'%Roga%') AND ( EXISTS (SELECT 
        1 AS [C1]
        FROM [dbo].[AspNetUsers] AS [Extent3]
        WHERE ([Extent1].[Id] = [Extent3].[OrganizationId]) AND (N'Petr' = [Extent3].[FirstName])
    ))

which means that the lambda func was parsed by the engine like a lambda expression. Due to what did this happen?
In general, I used to think that the nested lambda would be parsed here (in any way), which, in fact, happened. But here I just wanted to take out the nested function in a separate variable in order to use it further in Select. That is, I wanted to do this:
Func<User, bool> func = u => u.FirstName == "Petr";
            return _dbContext.Organizations.Where(o => o.Name.Contains("Roga") && o.Users.Any(func)).Select(...)

and then I thought, if I do this, then this will already be a specific call to the sysharp variable func from within the expression, and this should obviously lead to a crash. I checked this code and it really threw an exception
Internal .NET Framework Data Provider error 1025.

In general, my questions are:
1) any lambda nested in an Expression is also an expression, despite the fact that its type can be shown by the studio, like Func?
2) if so, why can't we move it into a separate variable to use it in several places, because then we use not a lambda in the external expression, but a reference to a variable with a lambda?
3) is there any way to solve the problem of the impossibility of making a nested lambda into a separate variable? It is not possible to declare a variable as an Expression, because Any on an ICollection does not accept an Expression.

Answer the question

In order to leave comments, you need to log in

2 answer(s)
F
footballer, 2017-02-13
@footballer

As I understand it, this problem of the impossibility of taking out a nested lambda into a separate variable is caused by the stupidity of the sisharp compiler? Because could he recognize at compile time that we are passing a Func variable to Any, and instead of an expression with a reference to the variable, generate an expression with a nested lambla that he would read from the variable at compile time? But instead it creates an expression with a reference to the variable, causing the parser to crash?

S
Stanislav Silin, 2017-02-13
@byme

I'm not sure, but my version is the following. In the first case, there is no lambda, there is a continuous expression of the following form:
o => o.Name.Contains("Roga") && o.Users.Any(u => u.FirstName == "Petr")
those. (u => u.FirstName == "Petr") is an expreshin nested within an expreshin.
In the second case, there is a real lyabda that is transmitted from outside, so it swears.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question