C# 实体框架可查询异步
我正在使用Entity Framework 6开发一些Web API,我的控制器方法之一是“全部获取”,它希望以C# 实体框架可查询异步,c#,entity-framework,async-await,C#,Entity Framework,Async Await,我正在使用Entity Framework 6开发一些Web API,我的控制器方法之一是“全部获取”,它希望以IQueryable的形式从数据库接收表的内容。在我的存储库中,我想知道是否有任何有利的理由异步执行此操作,因为我不熟悉将EF与async一起使用 基本上可以归结为 public async Task<IQueryable<URL>> GetAllUrlsAsync() { var urls = await context.Urls.ToListAs
IQueryable
的形式从数据库接收表的内容。在我的存储库中,我想知道是否有任何有利的理由异步执行此操作,因为我不熟悉将EF与async一起使用
基本上可以归结为
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
public异步任务GetAllUrlsAsync()
{
var url=await context.url.ToListAsync();
返回URL.AsQueryable();
}
vs
public IQueryable GetAllUrls()
{
返回context.url.AsQueryable();
}
异步版本真的会在这里带来性能上的好处吗?还是我会通过先投影到列表(使用async mind you)然后转到IQueryable而招致不必要的开销?在您发布的示例中有很大的不同,第一个版本:
var urls = await context.Urls.ToListAsync();
这是不好的,它基本上是从表中选择*,将所有结果返回到内存中,然后将where
应用到内存集合中,而不是对数据库执行select*from table where…
第二种方法实际上不会命中数据库,直到将查询应用于IQueryable
(可能通过linq.Where().Select()
样式的操作,该操作将只返回与查询匹配的db值)
如果您的示例具有可比性,则每个请求的async
版本通常会稍微慢一些,因为编译器生成的允许async
功能的状态机中有更多开销
然而,主要的区别(和好处)是
async
版本允许更多的并发请求,因为它在等待IO完成(db查询、文件访问、web请求等)时不会阻塞处理线程。问题似乎是您误解了async/Wait如何与实体框架一起工作
关于实体框架
那么,让我们看看这段代码:
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
var stuff1 = repo.GetStuff1ForUser(userId);
var stuff2 = repo.GetStuff2ForUser(userId);
return View(new Model(stuff1, stuff2));
使用我们得到的相同用法示例:
wait context.url.ToListAsync();
将存储在数据库中的所有十亿个URL加载到内存中public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
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();
}
使用System.Data.Entity;
公共可查询的GetAllUrls()
{
返回context.url.AsQueryable();
}
公共异步任务GetAllUrlsByUser(int userId){
return wait wait GetAllUrls().Where(u=>u.User.Id==userId.toListSync();
}
注意,您必须使用System.Data.Entity添加,才能将方法ToListSync()
用于IQueryable
请注意,如果您不需要过滤、分页等功能,则不需要使用IQueryable
。您只需使用wait context.url.tolistsync()
,并使用物化的列表
长话短说,
IQueryable
旨在推迟运行过程,首先与其他IQueryable
表达式一起构建表达式,然后将表达式作为一个整体进行解释和运行。
但是ToList()
method(或类似的几种方法)不能立即“按原样”运行表达式。
您的第一个方法(GetAllusAsync
)将立即运行,因为它是IQueryable
,后跟ToListSync()
方法。因此它立即(异步)运行,并返回一组IEnumerable
s。
同时,您的第二种方法(GetAllUrls
),将不会运行。相反,它返回一个表达式,此方法的调用方负责运行该表达式。@Korijn查看来自的图片,我可以说IIS中的所有请求都是以异步方式处理的,因为您不处理GetAllUrlsByUser
方法中的结果集,所以不需要使其异步。只需重新urn该任务并为自己保存一个不必要的状态机,使其不被编译器生成。@JohnathonSullinger尽管这样做可以顺利进行,但这难道不会产生任何异常不会在此处出现并传播到具有等待的第一个位置的副作用吗?(并不是说这很糟糕,但这是行为上的一个变化?)有趣的是,没有人注意到“About async/await”中的第二个代码示例是完全没有意义的,因为它会抛出一个异常,因为EF和EF核心都不是线程安全的,所以尝试并行运行只会抛出一个异常虽然这个答案是正确的,但如果您没有对列表执行任何操作,我建议避免使用async
和wait
。让调用方等待它。在此阶段等待调用时return wait GetAllUrls()。其中(u=>u.User.Id==userId)。toListSync();
您正在创建一个额外的异步wra
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();
}