ProgramingTip

Entity Framework 쿼리 가능합니다

bestdevel 2020. 10. 4. 12:02
반응형

Entity Framework 쿼리 가능합니다


Entity Framework 6을 사용하여 일부 웹 API 관련 작업을하고 컨트롤러 메소드 중 하나는 내 데이터베이스에서 테이블의 내용을 IQueryable<Entity>. 내 저장소에서 작성로 EF를 사용하는 것이 처음으로 수행되는 이유가 궁금합니다.

기본적으로 그것은

 public async Task<IQueryable<URL>> GetAllUrlsAsync()
 {
    var urls = await context.Urls.ToListAsync();
    return urls.AsQueryable();
 }

vs

 public IQueryable<URL> GetAllUrls()
 {
    return context.Urls.AsQueryable();
 }

어떤 버전은 실제로 여기에서 성능 이점을 얻을 수 있습니까? 아니면 먼저 목록에 투영하고 (비 동기화를 사용하여) IQueryable로 이동하여 불필요한 오버가 헤드를 입고 있습니까?


문제는 Entity Framework에서 async / await가 작동하는 방식을 오해 한 것입니다.

Entity Framework 정보

따라서 코드를 살펴 보겠습니다.

public IQueryable<URL> GetAllUrls()
{
    return context.Urls.AsQueryable();
}

그리고 그것의 사용 예 :

repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()

거기에서 무슨 일이 일어나나요?

  1. 우리는 IQueryable객체 를 얻습니다 (아직 데이터베이스에 접근하지 않음)repo.GetAllUrls()
  2. 다음을 IQueryable사용하여 지정된 조건으로 개체를 만듭니다..Where(u => <condition>
  3. IQueryable지정된 페이징 제한을 사용하여 개체를 만듭니다..Take(10)
  4. 를 사용하여 데이터베이스에서 결과를 검색합니다 .ToList(). 우리의 IQueryable수업은 (같은 ) sql로 성공 합니다. 그리고 데이터베이스는 데이터베이스를 사용할 수 있고 SQL Server는 10 개의 개체 만 보냅니다 (데이터베이스에 10 억 개의 URL 모두는 아님).select top 10 * from Urls where <condition>

좋아, 첫 번째 코드를 보자.

public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
    var urls = await context.Urls.ToListAsync();
    return urls.AsQueryable();
}

동일한 사용 예를 통해 다음을 얻었습니다.

  1. 을 사용하여 데이터베이스에 저작 억 개의 URL을 모두 메모리에로드하고 await context.Urls.ToListAsync();있습니다.

  2. 메모리 오버플로가 발생했습니다. 서버를 죽이는 올바른 방법

비동기 / 대기 정보

async / await가 선호되는 이유는 무엇입니까? 이 코드를 살펴 보겠습니다.

var stuff1 = repo.GetStuff1ForUser(userId);
var stuff2 = repo.GetStuff2ForUser(userId);
return View(new Model(stuff1, stuff2));

여기서 무슨 일이 일어나나요?

  1. 라인 1에서 시작 var stuff1 = ...
  2. 우리는 무언가를 얻고 자하는 SQL 서버에 요청을 보냅니다. userId
  3. 우리는 기다립니다 (현재 스레드가 차단됨)
  4. 우리는 기다립니다 (현재 스레드가 차단됨)
  5. .....
  6. SQL 서버는 우리에게 응답을 보냅니다.
  7. 2 호선으로 이동 var stuff2 = ...
  8. 우리는 무언가를 얻고 싶은 SQL 서버에 요청을 보냅니다. userId
  9. 우리는 기다립니다 (현재 스레드가 차단됨)
  10. 다시 한번
  11. .....
  12. SQL 서버는 우리에게 응답을 보냅니다.
  13. 뷰 렌더링

따라서 비동기 버전을 살펴 보겠습니다.

var stuff1Task = repo.GetStuff1ForUserAsync(userId);
var stuff2Task = repo.GetStuff2ForUserAsync(userId);
await Task.WhenAll(stuff1Task, stuff2Task);
return View(new Model(stuff1Task.Result, stuff2Task.Result));

여기서 무슨 일이 일어나나요?

  1. stuff1을 얻기 위해 SQL Server에 요청을 보냅니다 (1 행).
  2. stuff2를 얻기 위해 SQL Server에 요청을 보냅니다 (2 행).
  3. SQL 서버의 응답을 기다리지 만 현재 스레드가 차단되지 않고 다른 사용자의 쿼리를 처리 할 수 ​​있습니다.
  4. 뷰 렌더링

그것을하는 올바른 방법

여기에 좋은 코드 :

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>.


게시 한 예제 인 첫 번째 버전에는 큰 차이가 있습니다.

var urls = await context.Urls.ToListAsync();

이다 나쁜 , 그것은 기본적으로 수행 select * from table, 메모리에 모든 결과를 반환 한 후 적용 where메모리 수집보다는 일에 그 상대로을 select * from table where...데이터베이스에 대해.

두 번째 방법은 쿼리가 IQueryable(아마도 .Where().Select()쿼리와 일치하는 db 값만 반환 하는 linq 스타일 작업을 통해) 쿼리가 적용될 때까지 실제로 데이터베이스에 도달하지 않습니다 .

예제가 비교 가능한 경우 async컴파일러가 async기능 을 허용하기 위해 생성하는 상태 머신에 더 많은 오버 헤드가 있으므로 요청 당 버전이 일반적으로 약간 느려집니다 .

그러나 주요 차이점 (및 이점)은 asyncIO가 완료되기를 기다리는 동안 처리 스레드를 차단하지 않기 때문에 버전이 더 많은 동시 요청을 허용 한다는 것입니다 (db 쿼리, 파일 액세스, 웹 요청 등).

참고 URL : https://stackoverflow.com/questions/26676563/entity-framework-queryable-async

반응형