[英]Entity Framework Queryable async
I'm working on some some Web API stuff using Entity Framework 6 and one of my controller methods is a "Get All" that expects to receive the contents of a table from my database as IQueryable<Entity>
.我正在使用 Entity Framework 6 处理一些 Web API 的东西,我的控制器方法之一是“Get All”,它希望从我的数据库中接收表的内容作为
IQueryable<Entity>
。 In my repository I'm wondering if there is any advantageous reason to do this asynchronously as I'm new to using EF with async.在我的存储库中,我想知道是否有任何有利的理由异步执行此操作,因为我不熟悉使用 EF 和异步。
Basically it boils down to基本上它归结为
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
vs对比
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
Will the async version actually yield performance benefits here or am I incurring unnecessary overhead by projecting to a List first (using async mind you) and THEN going to IQueryable?异步版本实际上会在这里产生性能优势,还是我通过首先投影到列表(请注意使用异步)然后转到 IQueryable 来产生不必要的开销?
The problem seems to be that you have misunderstood how async/await work with Entity Framework.问题似乎是您误解了 async/await 如何与实体框架一起工作。
So, let's look at this code:那么,让我们看看这段代码:
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
and example of it usage:及其用法示例:
repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()
What happens there?那里会发生什么?
IQueryable
object (not accessing database yet) using repo.GetAllUrls()
repo.GetAllUrls()
获取IQueryable
对象(尚未访问数据库repo.GetAllUrls()
IQueryable
object with specified condition using .Where(u => <condition>
IQueryable
与指定条件使用对象.Where(u => <condition>
IQueryable
object with specified paging limit using .Take(10)
.Take(10)
创建一个具有指定分页限制的新IQueryable
对象.ToList()
..ToList()
从数据库中检索结果。 Our IQueryable
object is compiled to sql (like select top 10 * from Urls where <condition>
).IQueryable
对象被编译为 sql(例如select top 10 * from Urls where <condition>
)。 And database can use indexes, sql server send you only 10 objects from your database (not all billion urls stored in database) Okay, let's look at first code:好的,让我们看看第一个代码:
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
With the same example of usage we got:使用相同的用法示例,我们得到:
await context.Urls.ToListAsync();
await context.Urls.ToListAsync();
将存储在您的数据库中的所有 10 亿个 url 加载到内存中await context.Urls.ToListAsync();
. Why async/await is preferred to use?为什么首选使用 async/await? Let's look at this code:
让我们看看这段代码:
var stuff1 = repo.GetStuff1ForUser(userId);
var stuff2 = repo.GetStuff2ForUser(userId);
return View(new Model(stuff1, stuff2));
What happens here?这里会发生什么?
var stuff1 = ...
var stuff1 = ...
userId
userId
获取一些 stuff1var stuff2 = ...
var stuff2 = ...
userId
userId
获取一些 stuff2So let's look to an async version of it:所以让我们看看它的异步版本:
var stuff1Task = repo.GetStuff1ForUserAsync(userId);
var stuff2Task = repo.GetStuff2ForUserAsync(userId);
await Task.WhenAll(stuff1Task, stuff2Task);
return View(new Model(stuff1Task.Result, stuff2Task.Result));
What happens here?这里会发生什么?
So good code here:这么好的代码在这里:
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();
}
Note, than you must add using System.Data.Entity
in order to use method ToListAsync()
for IQueryable.请注意,您必须添加
using System.Data.Entity
才能将方法ToListAsync()
用于 IQueryable。
Note, that if you don't need filtering and paging and stuff, you don't need to work with IQueryable
.请注意,如果您不需要过滤和分页等内容,则不需要使用
IQueryable
。 You can just use await context.Urls.ToListAsync()
and work with materialized List<Url>
.您可以只使用
await context.Urls.ToListAsync()
并使用物化List<Url>
。
There is a massive difference in the example you have posted, the first version:您发布的示例有很大的不同,第一个版本:
var urls = await context.Urls.ToListAsync();
This is bad , it basically does select * from table
, returns all results into memory and then applies the where
against that in memory collection rather than doing select * from table where...
against the database.这很糟糕,它基本上是
select * from table
,将所有结果返回到内存中,然后将where
应用到内存集合中,而不是对数据库执行select * from table where...
。
The second method will not actually hit the database until a query is applied to the IQueryable
(probably via a linq .Where().Select()
style operation which will only return the db values which match the query.第二种方法实际上不会访问数据库,直到将查询应用于
IQueryable
(可能通过 linq .Where().Select()
样式操作,该操作只会返回与查询匹配的 db 值。
If your examples were comparable, the async
version will usually be slightly slower per request as there is more overhead in the state machine which the compiler generates to allow the async
functionality.如果您的示例具有可比性,则每个请求的
async
版本通常会稍微慢一些,因为编译器生成的状态机中有更多开销以允许async
功能。
However the major difference (and benefit) is that the async
version allows more concurrent requests as it doesn't block the processing thread whilst it is waiting for IO to complete (db query, file access, web request etc).然而,主要区别(和好处)是
async
版本允许更多并发请求,因为它在等待 IO 完成(数据库查询、文件访问、Web 请求等)时不会阻塞处理线程。
Long story short,长话短说,
IQueryable
is designed to postpone RUN process and firstly build the expression in conjunction with other IQueryable
expressions, and then interprets and runs the expression as a whole. IQueryable
旨在推迟 RUN 过程,首先与其他IQueryable
表达式一起构建表达式,然后将表达式作为一个整体进行解释和运行。
But ToList()
method (or a few sort of methods like that), are ment to run the expression instantly "as is".但是
ToList()
方法(或类似的几种方法)可以立即“按原样”运行表达式。
Your first method ( GetAllUrlsAsync
), will run imediately, because it is IQueryable
followed by ToListAsync()
method.您的第一个方法 (
GetAllUrlsAsync
) 将立即运行,因为它是IQueryable
后跟ToListAsync()
方法。 hence it runs instantly (asynchronous), and returns a bunch of IEnumerable
s.因此它立即运行(异步),并返回一堆
IEnumerable
。
Meanwhile your second method ( GetAllUrls
), won't get run.同时,您的第二种方法 (
GetAllUrls
) 不会运行。 Instead, it returns an expression and CALLER of this method is responsible to run the expression.相反,它返回一个表达式,此方法的 CALLER 负责运行该表达式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.