The problem seems to be that you misunderstood how async / await works with the Entity Framework.
About Entity Framework
So let's take a look at this code:
public IQueryable<URL> GetAllUrls() { return context.Urls.AsQueryable(); }
and an example of its use:
repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()
What is happening there?
- We get an
IQueryable object (not yet accessing the database) using repo.GetAllUrls() - We create a new
IQueryable with the specified condition using .Where(u => <condition> - We create a new
IQueryable with the specified swap limit using .Take(10) - We get the results from the database using
.ToList() . Our IQueryable compiled in sql (e.g. select top 10 * from Urls where <condition> ). And the database can use indexes, SQL Server sends you only 10 objects from your database (not all billions of URLs stored in the database)
Ok, let's look at the first code:
public async Task<IQueryable<URL>> GetAllUrlsAsync() { var urls = await context.Urls.ToListAsync(); return urls.AsQueryable(); }
With the same usage example, we got:
- We load into the memory all the billions of URLs stored in your database using
await context.Urls.ToListAsync(); , - We have a memory overflow. The right way to kill your server
About Async / Pending
Why is async / await preferable to use? Let's look at this code:
var stuff1 = repo.GetStuff1ForUser(userId); var stuff2 = repo.GetStuff2ForUser(userId); return View(new Model(stuff1, stuff2));
What's going on here?
- Starting at line 1
var stuff1 =... - We send a request to sql server that we want to get some things1 for
userId - Waiting (current thread is blocked)
- Waiting (current thread is blocked)
- ...
- Sql server send us an answer
var stuff2 =... to line 2 var stuff2 =...- We send a request to the sql server that we want to get some things2 for
userId - Waiting (current thread is blocked)
- And again
- ...
- Sql server send us an answer
- We pretend
So let's look at the asynchronous version:
var stuff1Task = repo.GetStuff1ForUserAsync(userId); var stuff2Task = repo.GetStuff2ForUserAsync(userId); await Task.WhenAll(stuff1Task, stuff2Task); return View(new Model(stuff1Task.Result, stuff2Task.Result));
What's going on here?
- We send a request to the sql server to get things1 (line 1)
- We send a request to the sql server to get things2 (line 2)
- We are waiting for responses from the SQL server, but the current thread is not blocked, it can handle requests from other users
- We pretend
The right way to do it
So the good code is here:
using System.Data.Entity; public IQueryable<URL> GetAllUrls() { return context.Urls.AsQueryable(); } public async Task<List<URL>> GetAllUrlsByUser(int userId) { return await GetAllUrls().Where(u => u.User.Id == userId).ToListAsync(); }
Note that you must add using System.Data.Entity in order to use the ToListAsync() method for IQueryable.
Please note that if you do not need filtering, paging, etc., you do not need to work with IQueryable . You can simply use await context.Urls.ToListAsync() and work with the materialized List<Url> .
Viktor Lova Oct 31 '14 at 14:29 2014-10-31 14:29
source share