为啥我们需要避免在 ASP.NET MVC 视图中直接使用 IQueryable?

Posted

技术标签:

【中文标题】为啥我们需要避免在 ASP.NET MVC 视图中直接使用 IQueryable?【英文标题】:Why do we need to avoid using IQueryable directly in an ASP.NET MVC view?为什么我们需要避免在 ASP.NET MVC 视图中直接使用 IQueryable? 【发布时间】:2016-03-10 15:15:49 【问题描述】:

有人告诉我,从 ASP.NET MVC 视图直接引用 IQueryable 是一种不好的做法,但我还没有找到任何明确的解释。有些人在访问视图中的 IQueryable 或 DbContext 时可能会遇到“已处置对象”错误,但这不是我关心的问题。

这是我如何实现一个简单的控制器和一个包含用户列表的视图

public ActionResult Index() 

    return View(db.Users);

在我看来

@model IEnumerable<User>

<ul>
@foreach (var user in Model)

    <li>@user.UserName</li>    

</ul>

有人告诉我,我应该返回一个“集合对象”,而不是将 IQueryable 传递给视图。

public ActionResult Index() 

    return View(db.Users.ToList());

我有点好奇为什么我必须这样做。我可以说后一种方法比我的第一种方法更糟糕,因为数据集在我的应用程序中迭代了两次,第一次通过ToList() 方法构造List 对象,第二次通过foreach 循环在我看来渲染&lt;li&gt; 物品。

所以这意味着后一种方法必须在我尚未发现的地方有更好的点。即使在 ASP.NET 论坛或 Microsoft MSDN 站点中,他们也提供了后一种方法的示例,但没有给出任何理由,还是我遗漏了什么?

谁能帮我解释一下这个简单的基本东西?

非常感谢。

【问题讨论】:

您不应该使用 IQueryable,因为您还没有具体化查询,所以每次具体化您都会调用数据库。如果您首先处理您的数据库并尝试实现您将无法访问底层数据库 @BradleyUffner 不会在他的视图中使用 IQueryable 而不是视图模型 因为控制器是为每个请求创建的,所以如果我将处理 DbContext 的逻辑添加到控制器的析构函数或 Dispose() 方法中(如果遵循 IDisposable 模式,那么只要控制器被配置。在您的控制器被处置并且您的视图被遗忘(例如,当您的应用程序正在关闭时)时保持 IQueryable 活动的意义是什么? ***.com/questions/5425920/… 【参考方案1】:

如果您传递IQueryable,您将创建一个leaky abstraction,您实际上可以在视图中移动业务逻辑,而不是严格呈现业务模型。这是因为 IQueryable 尚未针对数据库执行,这为视图留下了很多选项,例如过滤更多或检索其他属性等,这些确实应该在 Controller 中完成(好 Separation of Concerns)。

到你的第二点。如果您返回的对象太多以至于在循环中迭代它们(for/foreach/etc)会导致性能下降,而不是您遇到更大的问题,例如如何在 html 中发送/渲染那个巨大的列表正在构建与从数据库中实际检索此类列表相结合的瓶颈。

最后,您需要处理底层 DbContext。这可以在您的控制器被处置时向您的控制器注册以进行清理,但我可以看到许多程序员忘记这样做,从而留下打开的数据库连接。

【讨论】:

我明白了你关于关注点分离和泄漏抽象的想法。谢谢你的解释。如果我创建一个包装器 Enumerator 和一个扩展方法以将 IQueryable 强制转换为我的包装器 Enumerator 以便传递给视图的实例实际上是一个 IEnumerable 但在调用 foreach 或 GetEnumerator() 之前仍不执行查询,该怎么办? @NguyenThanhHa - 你为什么想要那个?迭代内存中的集合相当快。如果您的集合非常大,那么最慢的部分将是从数据库中检索而不是循环它。如果集合非常大并且因此 Db 检索非常慢,则应该向控制器添加某种类型的分页或过滤逻辑。我认为将一个打开的 IQueyrable 对象(无论是否包装)对视图没有任何好处,尤其是与性能无关。 视图可以是包含数据列表的JsonResult。它并不总是 HTML 视图。我担心的是,使用ToList() 意味着您将所有数据放入应用程序内存中,这看起来像是一种“缓冲”方式;这意味着内存密集型。在使用IQueryable 时,您正在遍历每个项目,一个一个地加载到应用程序内存中,这看起来像是一种“流式”方式。对吗? 好的,现在知道了。非常感谢您的关注和清晰的解释。 :) 我刚刚想到了一个案例,可以证明将IQueryable 传递给视图不是一个好习惯。考虑到有不同的开发人员在控制器和视图上工作的情况。因此,当将IQueryable 传递给视图时,在控制器上工作的开发人员将不知道开发人员将如何在他们的视图实现中使用该实例。因此,为了安全和良好的做法,他们应该提供一个集合对象,以避免一个数据库查询可以运行多次而不管视图开发人员如何运行的情况。

以上是关于为啥我们需要避免在 ASP.NET MVC 视图中直接使用 IQueryable?的主要内容,如果未能解决你的问题,请参考以下文章

如何避免 ASP.NET MVC 中的 HttpRequestValidationException 呈现导致异常的相同视图

这是错误的,为啥不是? ASP.NET MVC 视图包

使用 ASP.NET MVC,如何最好地避免同时编写添加视图和编辑视图?

ASP.NET MVC 新手:为啥在创建强类型视图时我的模型不可用?

为啥 ASP.NET MVC3 区域和 Razor 视图会产生此错误?

当我尝试使用 EF 在 ASP.NET MVC5 中查询视图时,为啥会收到错误“数据库 'ELITEPROD' 中的 CREATE TABLE 权限被拒绝”?