문제는 Entity Framework에서 async / await가 작동하는 방식을 오해 한 것 같습니다.
Entity Framework 정보
따라서이 코드를 살펴 보겠습니다.
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
그리고 그것의 사용 예 :
repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()
거기에서 무슨 일이 일어나나요?
- 우리는
IQueryable
객체 를 얻습니다 (아직 데이터베이스에 접근하지 않음)repo.GetAllUrls()
- 우리는 새로운
IQueryable
사용하여 지정된 조건 개체를.Where(u => <condition>
- 우리는 새로운
IQueryable
지정된 페이징 제한이 개체를.Take(10)
- 를 사용하여 데이터베이스에서 결과를 검색합니다
.ToList()
. 우리의 IQueryable
객체는 (같은 select top 10 * from Urls where <condition>
) sql로 컴파일됩니다 . 그리고 데이터베이스는 인덱스를 사용할 수 있으며 SQL Server는 데이터베이스에서 10 개의 개체 만 보냅니다 (데이터베이스에 저장된 10 억 개의 URL 모두는 아님).
좋아, 첫 번째 코드를 보자.
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
동일한 사용 예를 통해 다음을 얻었습니다.
- 을 사용하여 데이터베이스에 저장된 수십억 개의 URL을 모두 메모리에로드하고
await context.Urls.ToListAsync();
있습니다.
- 메모리 오버플로가 발생했습니다. 서버를 죽이는 올바른 방법
async / await 정보
async / await가 선호되는 이유는 무엇입니까? 이 코드를 살펴 보겠습니다.
var stuff1 = repo.GetStuff1ForUser(userId);
var stuff2 = repo.GetStuff2ForUser(userId);
return View(new Model(stuff1, stuff2));
여기서 무슨 일이 일어나나요?
- 라인 1에서 시작
var stuff1 = ...
- 우리는 몇 가지 물건을 얻고 싶은 SQL 서버에 요청을 보냅니다.
userId
- 우리는 기다립니다 (현재 스레드가 차단됨)
- 우리는 기다립니다 (현재 스레드가 차단됨)
- .....
- SQL 서버는 우리에게 응답을 보냅니다.
- 2 호선으로 이동
var stuff2 = ...
- 우리는 무언가를 얻고 싶은 SQL 서버에 요청을 보냅니다.
userId
- 우리는 기다립니다 (현재 스레드가 차단됨)
- 다시 한번
- .....
- SQL 서버는 우리에게 응답을 보냅니다.
- 뷰 렌더링
따라서 비동기 버전을 살펴 보겠습니다.
var stuff1Task = repo.GetStuff1ForUserAsync(userId);
var stuff2Task = repo.GetStuff2ForUserAsync(userId);
await Task.WhenAll(stuff1Task, stuff2Task);
return View(new Model(stuff1Task.Result, stuff2Task.Result));
여기서 무슨 일이 일어나나요?
- stuff1을 얻기 위해 SQL Server에 요청을 보냅니다 (1 행).
- stuff2를 얻기 위해 SQL Server에 요청을 보냅니다 (2 행).
- SQL 서버의 응답을 기다리지 만 현재 스레드가 차단되지 않고 다른 사용자의 쿼리를 처리 할 수 있습니다.
- 뷰 렌더링
그것을하는 올바른 방법
여기에 좋은 코드 :
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();
}
IQueryable에 대한 using System.Data.Entity
메서드를 사용 하려면 추가해야합니다 ToListAsync()
.
필터링 및 페이징 등이 필요하지 않은 경우 IQueryable
. await context.Urls.ToListAsync()
materialized를 사용 하고 작업 할 수 있습니다 List<Url>
.