我正在使用Entity Framework 6处理一些Web API,我的一个控制器方法是“Get All”,它希望从我的数据库中收到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();
}
异步版本实际上是否会在这里产生性能优势,或者我首先投射到List(使用async mind you)然后转到IQueryable会产生不必要的开销吗?
答案 0 :(得分:185)
问题似乎是您误解了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
对象被编译为sql(如select top 10 * from Urls where <condition>
)。并且数据库可以使用索引,sql server只向您发送数据库中的10个对象(不是存储在数据库中的所有亿个URL)好的,让我们看看第一个代码:
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
使用相同的使用示例:
await context.Urls.ToListAsync();
在内存中加载存储在您数据库中的所有十亿个网址。为什么首选使用async / await?我们来看看这段代码:
var stuff1 = repo.GetStuff1ForUser(userId);
var stuff2 = repo.GetStuff2ForUser(userId);
return View(new Model(stuff1, stuff2));
这里发生了什么?
var stuff1 = ...
userId
var stuff2 = ...
userId
让我们看看它的异步版本:
var stuff1Task = repo.GetStuff1ForUserAsync(userId);
var stuff2Task = repo.GetStuff2ForUserAsync(userId);
await Task.WhenAll(stuff1Task, stuff2Task);
return View(new Model(stuff1Task.Result, stuff2Task.Result));
这里发生了什么?
这里的代码很好:
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();
}
注意,您必须添加using System.Data.Entity
才能将方法ToListAsync()
用于IQueryable。
请注意,如果您不需要过滤和分页,那么您无需使用IQueryable
。您可以使用await context.Urls.ToListAsync()
并使用具体化的List<Url>
。
答案 1 :(得分:9)
您发布的示例与第一个版本存在巨大差异:
var urls = await context.Urls.ToListAsync();
这是错误,它基本上是select * from table
,将所有结果返回到内存中,然后对内存集合中的where
应用select * from table where...
而不是IQueryable
反对数据库。
在将查询应用于.Where().Select()
之前,第二种方法实际上不会访问数据库(可能通过linq async
样式操作,该操作只返回与查询匹配的db值。
如果您的示例具有可比性,async
版本的每个请求通常会稍微慢一些,因为编译器生成的状态机有更多的开销以允许async
功能。
然而,主要区别(和好处)是{{1}}版本允许更多并发请求,因为它在等待IO完成时不会阻止处理线程(db查询,文件访问,Web请求)等)。
答案 2 :(得分:0)
长话短说,
IQueryable
旨在推迟RUN过程,并首先与其他IQueryable
表达式一起构建该表达式,然后整体解释并运行该表达式。
但是ToList()
方法(或类似的几种方法)可以立即按原样运行表达式。
您的第一个方法(GetAllUrlsAsync
)将立即运行,因为它是IQueryable
后跟ToListAsync()
方法。因此它会立即(异步)运行,并返回一堆IEnumerable
。
同时,您的第二种方法(GetAllUrls
)将不会运行。而是返回一个表达式,此方法的CALLER负责运行该表达式。