I wanted to use a Select()
method but it seems like Select()
doesn't really accept the await keyword. Therefore I was asking myself the question, should I keep using Eager Loading asynchronously or actually the Select method is very good synchronously and will do the job as efficiently as the other ?
I use the Select()
to map a very large entity to a DTO and Eager Loading to basically mimic the Select()
method by creating a bunch of method including a few relationships such as GetObjectWithPrice
and GetObjectWithPriceAndDate
and so that's why I was asking for the use of a Select()
method instead but the synchronicity worries me.
EDIT
To answer @AvrohomYisroel, here's what I've been doing with my code so far:
public async Task<IReadOnlyList<Book>> GetAllBooksWithRelatedDataAsync()
{
var books = await context.Books
.AsNoTracking()
.AsSplitQuery()
.Include(d => d.Price)
.Include(d => d.Images)
.Include(d => d.Author)
.ToListAsync();
return books;
}
That is how I use Eager Loading asynchronously. And I've been questioning if when using Select()
, I was supposed to expect it to be used the same way in terms of asynchronicity. However I might be completely clouded in that the use of Select()
is synchronous because it works a different way that I thought it did.
CodePudding user response:
.Select()
or projections are generally the most efficient form of data retrieval if performed on the server, but when we use projections we are precluding the use of includes. .Select()
will change the shape of the object graph, if you are not changing the shape, then just use .Include()
, .AsSplitQuery()
gives you the best performance when you need to include related entities because it will load each of the navigation path as an individual query.
.Select()
is frequently used in repository patterns to map data models into DTOs, there are other libraries you can use that can simplify this that will internally call.Select()
.- As of Core 6, LINQ to Entity Projections also support
.AsSplitQuery()
which means that performance is now less of a concern when choosing between Eager Loading and Projections.
The point of projecting is to pull back not just the required related navigation entities, but only the specific fields that we need. .Select()
is therefor an even more eager form of loading than .Include()
, but allows for you to be selective about which fields to load.
But we can't have both .Select()
and .Include()' in a server expression, in fact any
.Include()expressed before the
.Select()will be ignored unless the
.IQueryable()has been loaded into an
.IEnumerable()` first.
As to asynchronicity, there is no difference between .Select()
or .Include()
if you apply them to an IQueryable<T>
expression, the following would still be asynchronous:
var books = await context. Books
.Select(b => new BookDTO {
ISBN = b.BookNumber,
Title = b.Title,
Author = b.Author.Name,
Price = b.Price,
Images = b.Images.Select(i => i.Url)
})
.AsSplitQuery()
.ToListAsync();
The only time that .Select()
might constrain you to a synchronous context is if in your projection you have used a function or logic that cannot be converted to a data store (SQL) expression. In this instance EF Core will autmatically evaluate your expression to bring data into memory and then it will perform the .Select()
. At that point you are dealing with IEnumerable<T>
and synchronous evaluations.