Client evaluation in top-level EF Core projection

128 views Asked by At

Suppose I perform a query and project into an anonymous type; that would perform evaluation on the server:

var people = await context
  .People
  .OrderBy(x => x.Name)
  .Select(x => new { x.Id, x.Surname, x.Forename })
  .ToListAsync();

But suppose I perform a query and need to project into a concrete type.

Option 1: map results into concrete type:

var people = (await context
  .People
  .OrderBy(x => x.Name)
  .Select(x => new { x.Id, x.Surname, x.Forename })
  .ToListAsync())
  .Select(x => new PersonDto(x.Id, x.Surname, x.Forename));

Option 2: use concrete type in "top-level projection":

var people = await context
  .People
  .OrderBy(x => x.Name)
  .Select(x => new PersonDto(x.Id, x.Surname, x.Forename));
  .ToListAsync()

About (2), the docs state that in a top-level projection, some evaluation is performed in the database and some on the client. (But the docs show an example of a client function in an anonymous type, so it differs to my use case.)

In this specific example, am I right in assuming that all evaluation is performed on the server, and that (2) is just a short-cut for (1), i.e. they are functionally equivalent?

1

There are 1 answers

0
dani herrera On BEST ANSWER

Question:

all evaluation is performed on the server, and that (2) is just a short-cut for (1), i.e. they are functionally equivalent?

Answer

Obviously, the PersonDto object is instantiated on client side, but, EF is able to get the three constructor parameters values, x.Id, x.Surname, x.Forename from the database. Just this 3 values ( Select id, surname, forename from people order by name ):

EF Core supports partial client evaluation in the top-level projection (essentially, the last call to Select()). If the top-level projection in the query can't be translated to the server, EF Core will fetch any required data from the server and evaluate remaining parts of the query on the client. If EF Core detects an expression, in any place other than the top-level projection, which can't be translated to the server, then it throws a runtime exception

Full explanation at https://learn.microsoft.com/en-us/ef/core/querying/client-eval docs.

Test

Here a sample of EF translating top-level projection to SQL. Expression:

var result = 
    dbcontext.
    Customers.
    Select(x => new PersonName2CharsDto(x.Name.Substring(0,2))).
    ToList();

The generated sql (for sqlite):

SELECT substr("c"."Name", 0 + 1, 2)
FROM "Customers" AS "c"