实体框架可查询异步 [英] Entity Framework Queryable async

查看:145
本文介绍了实体框架可查询异步的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Entity Framework 6处理一些Web API的东西,我的一个控制器方法是一个Get All,它希望从我的数据库中收到表的内容,如 IQueryable<实体GT; 。在我的存储库中,我想知道是否有任何有利的理由像异步使用EF这样做是异步的。



基本上归结为



  public async任务< IQueryable< URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}

vs

  public IQueryable< URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}

异步版本是否会在这里实际产生性能优势,或者我会产生不必要的开销首先投射到列表(使用异步头脑),然后转到IQueryable?

解决方案

问题似乎是你有误解了实体框架的异步/等待工作。



关于实体框架



所以,让我们看看这个代码:

  public IQueryable< URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}

及其用法示例:


$ b $($)$($)$($)$($)$($)$($)$($)$ >

发生什么事?


  1. 我们正在使用 IQueryable 对象(不访问数据库) $ c> repo.GetAllUrls()

  2. 我们使用指定的条件创建一个新的 IQueryable code> .Where(u =>< condition>

  3. 我们创建一个新的 IQueryable 具有指定分页限制的对象,使用 .Take(10)

  4. 我们使用从数据库中检索结果。 ToList()。我们的 IQueryable 对象被编译为sql(如从Urls中选择前10个*,其中< condition>数据库可以使用索引,sql server只发送数据库中的10个对象(并不是数据库中存储的所有十亿个URL)

好的,我们来看看第一个代码:

  public async任务< IQueryable< URL>> GetAllUrlsAsync ()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}

与我们得到的使用情况相同:


  1. 我们正在使用 await context.Urls.ToListAsync();

  2. 加载内存中存储在您的数据库中的所有十亿个URL溢出。杀死你的服务器的正确方法



关于异步/等待



/等待优先使用?我们来看看这个代码:

  var stuff1 = repo.GetStuff1ForUser(userId); 
var stuff2 = repo.GetStuff2ForUser(userId);
return View(new Model(stuff1,stuff2));

这里发生了什么?


  1. 从第1行开始 var stuff1 = ...

  2. 我们向sql server发送请求,我们不要获得一些东西1 userId

  3. 我们等待(当前线程被阻止)

  4. 我们等待(当前线程被阻止)

  5. ...

  6. Sql server发送给我们回复

  7. 我们移动到第2行 var stuff2 = ...

  8. 我们发送请求到sql server,我们不会得到一些stuff2为 userId

  9. 我们等待(当前线程被阻止)

  10. 再次

  11. ...

  12. Sql server发送给我们回复

  13. 我们呈现视图

所以让我们来看看它的异步版本:

  var stuff1Task = repo.GetStuff1ForUserAsync(userId); 
var stuff2Task = repo.GetStuff2ForUserAsync(userId);
等待Task.WhenAll(stuff1Task,stuff2Task);
return View(new Model(stuff1Task.Result,stuff2Task.Result));

这里发生了什么?


  1. 我们向sql server发送请求以获取stuff1(第1行)

  2. 我们向sql server发送请求以获取stuff2(第2行)

  3. 我们等待sql server的响应,但是当前线程不被阻止,他可以处理来自其他用户的查询。

  4. 我们呈现视图



    1. 正确的方法



      这里很好的代码:

       使用System.Data.Entity; 

      public IQueryable< URL> GetAllUrls()
      {
      return context.Urls.AsQueryable();
      }

      public async任务<列表< URL>> GetAllUrlsByUser(int userId){
      return await GetAllUrls()。其中​​(u => u.User.Id == userId).ToListAsync();
      }

      请注意,不要使用System.Data添加 .Entity 为了使用IQueryable的方法 ToListAsync()。注意,如果您不需要过滤和分页和东西,则不需要使用 IQueryable 。您可以使用等待context.Urls.ToListAsync()并使用实例化的列表< Url>


      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>. 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.

      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?

      解决方案

      The problem seems to be that you have misunderstood how async/await work with Entity Framework.

      About Entity Framework

      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?

      1. We are getting IQueryable object (not accessing database yet) using repo.GetAllUrls()
      2. We create a new IQueryable object with specified condition using .Where(u => <condition>
      3. We create a new IQueryable object with specified paging limit using .Take(10)
      4. We retrieve results from database using .ToList(). Our IQueryable object is compiled to sql (like 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:

      1. We are loading in memory all billion urls stored in your database using await context.Urls.ToListAsync();.
      2. We got memory overflow. Right way to kill your server

      About async/await

      Why async/await is preferred to use? 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?

      1. Starting on line 1 var stuff1 = ...
      2. We send request to sql server that we wan't to get some stuff1 for userId
      3. We wait (current thread is blocked)
      4. We wait (current thread is blocked)
      5. ...
      6. Sql server send to us response
      7. We move to line 2 var stuff2 = ...
      8. We send request to sql server that we wan't to get some stuff2 for userId
      9. We wait (current thread is blocked)
      10. And again
      11. ...
      12. Sql server send to us response
      13. We render view

      So 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?

      1. We send request to sql server to get stuff1 (line 1)
      2. We send request to sql server to get stuff2 (line 2)
      3. We wait for responses from sql server, but current thread isn't blocked, he can handle queries from another users
      4. We render view

      Right way to do it

      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.

      Note, that if you don't need filtering and paging and stuff, you don't need to work with IQueryable. You can just use await context.Urls.ToListAsync() and work with materialized List<Url>.

      这篇关于实体框架可查询异步的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆