Question3r
Question3r

Reputation: 3752

How to query data asynchronously with Ef Core?

Imagine you have a variable databaseContext and want to select all users from the users table. From the docs

https://learn.microsoft.com/en-us/ef/core/querying/

you might create this method:

public IEnumerable<User> GetUsers() => _databaseContext.Users;

but isn't that synchronous code? And I think this is completely wrong:

public Task<User[]> GetUsers() => _databaseContext.Users.ToArrayAsync();

I would expect a method like this:

public Task<IEnumerable<User>> GetUsers() => _databaseContext.Users /* but select them asynchronously */;

Since EF Core provides synchronous and asynchronous methods e.g. Find and FindAsync I don't know how to query all data asynchronously. Maybe EF Core does this under the hood somehow but then I should have to await it, right?

Upvotes: 1

Views: 3391

Answers (1)

atiyar
atiyar

Reputation: 8338

  1. The following code will give you the list of all users asynchronously -
public async Task<IEnumerable<User>> GetUsers() => await _databaseContext.Users.ToListAsync();

Here the async keyword identifies the method as an asynchronous method (so that you can call it accordingly) and the await keyword makes sure - i) the calling thread isn't blocked and, ii) the result is awaited. Which thread (the calling one itself or a separate one) receives the awaited result later depends on how the Task Library handles async/await operations under-the-hood. And that exactly is what the Task Library is intended to do - taking away the concept of thread from the developers' hand forcing them to think in terms of Task, a higher level abstraction it provides instead.

For more -
Dissecting the async methods in C#
https://stackoverflow.com/a/42291461/446519

  1. This -
public IEnumerable<User> GetUsers() => _databaseContext.Users;

is a synchronous method, but more importantly it doesn't query the database. To query the database and then return the list of all users (synchronously) call ToList() -

public IEnumerable<User> GetUsers() => _databaseContext.Users.Tolist();
  1. This -
public Task<User[]> GetUsers() => _databaseContext.Users.ToArrayAsync();

is also a synchronous method and it will return a Task<User[]> instance immediately. But you can use the returned task object later, to get the result asynchronously, like -

Task<User[]> task = this.GetUsers();
User[] users = task.GetAwaiter().GetResult();

EDIT :
In point No.3 above, the code used after returning the Task<User[]> is purely to demonstrate the fact - "its your Task object now and you can use it later to get the result from it asynchronously". Showing what approach you should or shouldn't use for doing that was not the intention.

As @pinkfloydx33 suggested in the comment, .GetAwaiter().GetResult() might not always be the best approach to get the result. You can do the same without being explicit about getting the awaiter as -

Task<User[]> myTask = this.GetUsers();
User[] users = await myTask;

Upvotes: 2

Related Questions