Có vẻ như vấn đề là bạn đã hiểu sai cách async / await hoạt động với Entity Framework.
Giới thiệu về Khung thực thể
Vì vậy, hãy xem mã này:
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
và ví dụ về cách sử dụng nó:
repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()
Điều gì xảy ra ở đó?
- Chúng tôi đang nhận được
IQueryable
đối tượng (chưa truy cập cơ sở dữ liệu) bằng cách sử dụngrepo.GetAllUrls()
- Chúng tôi tạo một
IQueryable
đối tượng mới với điều kiện được chỉ định bằng cách sử dụng.Where(u => <condition>
- Chúng tôi tạo một
IQueryable
đối tượng mới với giới hạn phân trang được chỉ định bằng cách sử dụng.Take(10)
- Chúng tôi lấy kết quả từ cơ sở dữ liệu bằng cách sử dụng
.ToList()
. IQueryable
Đối tượng của chúng ta được biên dịch thành sql (like select top 10 * from Urls where <condition>
). Và cơ sở dữ liệu có thể sử dụng các chỉ mục, máy chủ sql chỉ gửi cho bạn 10 đối tượng từ cơ sở dữ liệu của bạn (không phải tất cả tỷ url được lưu trữ trong cơ sở dữ liệu)
Được rồi, hãy xem mã đầu tiên:
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
Với cùng một ví dụ về cách sử dụng, chúng tôi nhận được:
- Chúng tôi đang tải trong bộ nhớ tất cả tỷ url được lưu trữ trong cơ sở dữ liệu của bạn bằng cách sử dụng
await context.Urls.ToListAsync();
.
- Chúng tôi bị tràn bộ nhớ. Cách đúng để giết máy chủ của bạn
Giới thiệu về async / await
Tại sao async / await được ưu tiên sử dụng? Hãy xem mã này:
var stuff1 = repo.GetStuff1ForUser(userId);
var stuff2 = repo.GetStuff2ForUser(userId);
return View(new Model(stuff1, stuff2));
chuyện gì xảy ra ở đây thế?
- Bắt đầu từ dòng 1
var stuff1 = ...
- Chúng tôi gửi yêu cầu đến máy chủ sql mà chúng tôi muốn nhận một số nội dung1 cho
userId
- Chúng tôi đợi (chuỗi hiện tại bị chặn)
- Chúng tôi đợi (chuỗi hiện tại bị chặn)
- .....
- Máy chủ Sql gửi phản hồi cho chúng tôi
- Chúng tôi chuyển sang dòng 2
var stuff2 = ...
- Chúng tôi gửi yêu cầu đến máy chủ sql mà chúng tôi muốn nhận một số thứ2 cho
userId
- Chúng tôi đợi (chuỗi hiện tại bị chặn)
- Và một lần nữa
- .....
- Máy chủ Sql gửi phản hồi cho chúng tôi
- Chúng tôi kết xuất chế độ xem
Vì vậy, hãy xem xét một phiên bản không đồng bộ của nó:
var stuff1Task = repo.GetStuff1ForUserAsync(userId);
var stuff2Task = repo.GetStuff2ForUserAsync(userId);
await Task.WhenAll(stuff1Task, stuff2Task);
return View(new Model(stuff1Task.Result, stuff2Task.Result));
chuyện gì xảy ra ở đây thế?
- Chúng tôi gửi yêu cầu đến máy chủ sql để lấy thứ1 (dòng 1)
- Chúng tôi gửi yêu cầu đến máy chủ sql để lấy thứ 2 (dòng 2)
- Chúng tôi chờ phản hồi từ máy chủ sql, nhưng luồng hiện tại không bị chặn, anh ấy có thể xử lý các truy vấn từ người dùng khác
- Chúng tôi kết xuất chế độ xem
Đúng cách để làm điều đó
Vì vậy, mã tốt ở đây:
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();
}
Lưu ý, hơn bạn phải thêm using System.Data.Entity
để sử dụng phương thức ToListAsync()
cho IQueryable.
Lưu ý rằng nếu bạn không cần lọc và phân trang và nội dung, bạn không cần phải làm việc với IQueryable
. Bạn chỉ có thể sử dụng await context.Urls.ToListAsync()
và làm việc với materialized List<Url>
.