【问题标题】:Why is auto generated controller Get() method synchronous?为什么自动生成的控制器 Get() 方法是同步的?
【发布时间】:2019-01-03 13:58:13
【问题描述】:

使用Entity创建基本的ASP.NET Core 2.1 MVC API web应用程序时,可以在选择模型和dbContext后自动生成控制器(RMB -> Add -> Controller... -> API Controller with actions, using Entity Framework) .

我想知道为什么生成的所有方法都是异步的,除了返回所有表的基本 Get() ?这是一个错误吗?这是一个数据库调用,因此我希望它是异步的。在这种情况下使用异步调用没有真正的好处吗?如果是,为什么?

    // GET: api/User
    [HttpGet]
    public IEnumerable<User> GetUsers()
    {
        return _context.Users;
    }

【问题讨论】:

  • 这只是一些基于默认模板的代码。您必须编写您的自己的 代码来做您想做的事。在签名中粘贴async 也不会使该方法异步运行。 async 只是语法糖,它允许使用 await 来等待 已经 异步操作,例如 ToListAsync()

标签: c# .net asp.net-mvc asp.net-core .net-core


【解决方案1】:

return _context.Users 只返回一个DbSet&lt;Users&gt; 类型的对象。它不会对其进行迭代或做任何工作,它只是传递一个允许您访问数据库数据的对象。

DbSet&lt;T&gt; 也是一个IQueryable&lt;T&gt;,这意味着在您调用某种执行函数(如.ToList().Single(x=&gt;x.Id == idToLookFor))之前不会调用数据库

如果您要异步迭代它,那么您将有一个异步 Get() 方法,例如;

return await _context.Users.ToListAsync()


更新

我意识到实际上并没有回答你的问题,

您不太可能想要归还整张桌子。 (SELECT * FROM [Users]) 那么这里的 Get() 方法是一种反模式(在我看来**),称为“暴露 IQueryable”

所以在你的控制器中你可以做类似的事情

_context.Get().Where(user=&gt;user.FirstName == 'Steve').ToList()

或者你可以让它异步,就像你认为你应该在数据库调用中做的那样

await _context.Get().Where(user=&gt;user.FirstName == 'Steve').ToListAsync()

那么,模板生成Get()是不是出错了?不,但我认为您不应该将 IQueryable 公开为公共方法,所以我不同意。


IQueryable&lt;T&gt;

var query = _context.Users;  //SQL:  * FROM [Users]

query = query.Where(x=>x.Name == "Steve"); 
//SQL: * FROM [Users] WHERE Name = 'Steve'

query = query.Where(x=>x.wearsHats == true);
//SQL: * FROM [Users] WHERE Name = 'Steve' AND WearsHats = true

query = query.Select(x=>x.Name);
//SQL: Name FROM [Users] WHERE Name = 'Steve' AND WearsHats = true

var result = query.ToList()
//SQL: SELECT Name FROM [Users] WHERE Name = 'Steve' AND WearsHats = true

【讨论】:

  • _context.Users 缓存了吗? .ToList() 到底发生了什么变化?它仍在请求数据库中的记录,并返回您必须从数据库中获取的相同数据(用户)。
  • 我不是 EF 专家,在幕后发生的事情比我关心的要多得多,但要点是 DbSet 在您要求 之前是空的。当您请求 something 时,它会被转换为 SQL 查询,该查询会从数据库中获取您的 something,并将您的 something 类型传递给您你要求的。
  • 如果我理解你所说的正确,那么暴露裸露的DbSet&lt;Users&gt; 不应该给你任何结果(你没有要求任何东西),但它有效正确 - 它从数据库中提供用户表数据。怎么会这样?
  • 我的时间不多了,请检查更新。我添加了一些可能有帮助也可能没有帮助的伪代码。值得注意的是...... EF 核心并没有真正生成“SQL 命令的一部分”,它只是作为解释性的存在。
  • @Peace MVC 基础架构将在将响应写入客户端时迭代您从控制器返回的集合。所以查询执行被延迟到那时。 IMO 最好自己使用ToList() 或更优选await _context.Users.ToListAsync() 执行查询。
【解决方案2】:

是的,这与所有其他异步操作非常不一致。我不认为有任何具体原因。我认为微软只是忘记更新模板的这一部分。我希望生成以下内容:

// GET: api/User
[HttpGet]
public async Task<ActionResult<IEnumerable<User>>> GetUsers()
{
    return await _context.Users.ToListAsync();
}

即使是单线操作仍然可以从异步中受益 - IIS 线程在数据库查询期间不会被阻塞。您可以在关于 SO 的其他问题中阅读更多相关信息,例如:When should I use Async Controllers in ASP.NET MVC?

编辑:您可以在github 上找到模板源。您可以在第 38-42 行中看到,在最新版本中,此操作已更新,并将以异步方式生成,与我在上面所写的完全一样。

【讨论】:

  • 我认为模板没有过时。所有其他方法都是用现代模式编写的,并且看起来是最新的。整个模板不久前更新。我认为微软这样做一定是有原因的。
  • @Peace 我猜“过时的模板”并不是完全正确的表达方式。我的意思是我认为微软忘记更新这个特定操作的模板部分。所有其他动作都以现代方式编写的事实使这个孤独的同步动作看起来更加格格不入。
猜你喜欢
  • 2012-12-27
  • 2018-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-05
相关资源
最近更新 更多