entity framework可查询asynchronous
我正在使用entity framework6一些Web API的东西,我的控制器方法之一是“Get All”,希望从IQueryable<Entity>
从我的数据库接收表的内容。 在我的存储库中,我想知道是否有任何有利的原因asynchronous做这个,因为我是新来使用EF与asynchronous。
基本上归结为
public async Task<IQueryable<URL>> GetAllUrlsAsync() { var urls = await context.Urls.ToListAsync(); return urls.AsQueryable(); }
VS
public IQueryable<URL> GetAllUrls() { return context.Urls.AsQueryable(); }
asynchronous版本实际上会在这里产生性能优势,还是我通过首先投影到列表(使用asynchronous介意你)然后去IQueryable?
这个问题似乎是你误解了如何asynchronous/等待与entity framework的工作。
关于entity framework
所以,让我们看看这个代码:
public IQueryable<URL> GetAllUrls() { return context.Urls.AsQueryable(); }
和它的用法的例子:
repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()
那里发生了什么?
- 我们正在使用
repo.GetAllUrls()
获取IQueryable
对象(不访问数据库repo.GetAllUrls()
- 我们使用
.Where(u => <condition>
创build一个具有指定条件的新的IQueryable
对象 - 我们使用
.Take(10)
创build一个新的具有指定分页限制的IQueryable
对象。 - 我们使用
.ToList()
从数据库检索结果。 我们的IQueryable
对象被编译为sql(就像select top 10 * from Urls where <condition>
)。 和数据库可以使用索引,SQL服务器从您的数据库只发送10个对象(不是所有的数据库中存储的十亿个URL)
好吧,让我们看看第一个代码:
public async Task<IQueryable<URL>> GetAllUrlsAsync() { var urls = await context.Urls.ToListAsync(); return urls.AsQueryable(); }
用同样的例子,我们得到了:
- 我们正在使用
await context.Urls.ToListAsync();
在内存中加载存储在数据库中的所有十亿个URLawait context.Urls.ToListAsync();
。 - 我们有内存溢出。 正确的方式来杀死你的服务器
关于asynchronous/等待
为什么asynchronous/等待是首选使用? 让我们看看这个代码:
var stuff1 = repo.GetStuff1ForUser(userId); var stuff2 = repo.GetStuff2ForUser(userId); return View(new Model(stuff1, stuff2));
这里发生了什么?
- 从第1行开始
var stuff1 = ...
- 我们发送请求到sql服务器,我们不想得到一些stuff1
userId
- 我们等待(当前线程被阻止)
- 我们等待(当前线程被阻止)
- …
- Sql服务器发给我们回应
- 我们移动到第2行
var stuff2 = ...
- 我们发送请求到SQL服务器,我们不能得到一些stuff2
userId
- 我们等待(当前线程被阻止)
- 然后再次
- …
- Sql服务器发给我们回应
- 我们渲染视图
所以我们来看看它的一个asynchronous版本:
var stuff1Task = repo.GetStuff1ForUserAsync(userId); var stuff2Task = repo.GetStuff2ForUserAsync(userId); await Task.WhenAll(stuff1Task, stuff2Task); return View(new Model(stuff1Task.Result, stuff2Task.Result));
这里发生了什么?
- 我们发送请求到SQL服务器获取stuff1(第1行)
- 我们发送请求到SQL服务器获取stuff2(第2行)
- 我们等待来自sql server的响应,但是当前线程没有被阻塞,他可以处理来自其他用户的查询
- 我们渲染视图
正确的方法来做到这一点
这么好的代码在这里:
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的ToListAsync()
方法,必须添加using System.Data.Entity
。
请注意,如果您不需要过滤和分页以及其他东西,则不需要使用IQueryable
。 您可以使用await context.Urls.ToListAsync()
并使用物化List<Url>
。
你发布的例子有一个巨大的差异,第一个版本:
var urls = await context.Urls.ToListAsync();
这是不好的 ,它基本上select * from table
中select * from table
,将所有结果返回到内存中,然后应用内存集合中的where
一个,而不是对数据库执行select * from table where...
第二种方法直到查询被应用到IQueryable
(实际上可能通过一个只返回与查询相匹配的db值的linq。
如果您的示例具有可比性,则每个请求的async
版本通常会稍慢一些,因为编译器生成的允许async
function的状态机的开销更大。
然而,主要的区别(和好处)是, async
版本允许更多的并发请求,因为它在等待IO完成(db查询,文件访问,web请求等)时不阻塞处理线程。