何时在 MVC 6 的视图中使用 @await Html.PartialAsync

Posted

技术标签:

【中文标题】何时在 MVC 6 的视图中使用 @await Html.PartialAsync【英文标题】:When to use @await Html.PartialAsync in a View in MVC 6 【发布时间】:2015-02-24 20:59:55 【问题描述】:

我在 Scott Hanselman 的一个博客中注意到,他在使用 .Net 5 (MVC 6) 时在其视图中使用了以下代码:

@await html.PartialAsync("_LoginPartial")

对比

@Html.Partial("_LoginPartial")

是否有任何文档说明何时应该使用哪一个?

【问题讨论】:

【参考方案1】:

这实际上是一个非常有趣的问题和场景。在某种程度上,异步是新的热点(尽管它真的不是那么新)。 Entity Framework 6 使用异步方法和每个...单个...片段...文档...突然开始对所有内容使用异步。我认为我们在这里看到了一些相同的东西。 MVC 6 支持异步渲染部分内容,所以 OMG,我们现在都不得不使用异步。

异步服务于一个非常具体的目的。它允许在当前任务处于等待状态时将活动线程返回到池中以处理其他任务。其中的关键部分是“等待状态”。某些任务与异步完全不兼容。像复杂的财务分析这样的 CPU 密集型工作永远不会允许线程进入等待状态,因此即使您将其设置为异步,一切都可以有效地同步运行。另一方面,涉及网络延迟(从 Web API 请求资源、查询数据库等)或 I/O 绑定(读/写文件等)的事情有时可能会有线程在在继续处理之前等待其他一些进程完成。

特别是在渲染部分时,唯一不完全受 CPU 限制的部分是从文件系统中读取视图文件本身。虽然 技术上 足以使其符合异步条件,但读取本质上可能小于 50KB 的文本文件实际上需要多长时间。当线程被交还给池时,可能是时候请求它回来了,所以此时您实际上使用资源的效率更低。

总之,不要陷入“可以异步,所以我必须异步”的陷阱。每次使用都应该根据它是否有实际价值来评估。异步有很多开销,如果您只谈论几毫秒的等待时间,那么可能不值得所有额外的开销。

【讨论】:

实际上,await 会产生当前线程,直到任务完成。这与等待状态无关。 首先,我不是专门讨论async 关键字,这里只是一般的异步。其次,我不知道你最后的陈述是什么意思。它与处于等待状态有关。线程不仅仅是自动产生的。它必须一直工作,直到它等待超出其控制范围的东西:数据通过网络传输,硬盘驱动器旋转,等等。一旦不再等待,代码就需要返回线程来完成正在进行的任何工作。 不一定是“错误的”。渲染部分需要访问文件系统。文件系统有一个队列,根据当时服务器上发生的其他情况,可能会涉及等待时间。任何可能需要等待的操作都符合异步条件,因此在这方面,拥有PartialAsync 之类的东西是完全有效的。我的观点是,在大多数环境和情况下,使用 async 来渲染部分会弄巧成拙,并且在使用 async 时,您应该始终评估您的特定场景。不要只使用异步,因为它就在那里。 @ta.speot.is:确实如此。然而,这不是重点。如果您正在执行异步操作,那么是的,使用异步,但问题是教程和此类往往不会做出区分,并且消息灵通的开发人员会产生您应该只使用异步的印象,因为这就是教程的内容,书,视频等。如果您只是渲染一个视图,那么异步执行可能不是正确的方法。 是的。视图组件是异步的,如果您的部分执行任何异步操作,它本身应该是异步的。老实说,您可能会更频繁地使用异步。我只是指出,在某些情况下,您实际上可能最好选择同步,因为大多数开发人员实际上并没有逐案考虑。【参考方案2】:

根据关于部分视图的 ASP.NET MVC 文档。 https://docs.asp.net/en/latest/mvc/views/partial.html

PartialAsync 方法可用于包含异步代码的局部视图(尽管通常不鼓励视图中的代码):

还有页面上的注释。

如果您的视图需要执行代码,推荐的模式是使用视图组件而不是局部视图。

所以你应该使用Partial 并避免使用PartialAsync,如果你发现自己使用PartialAsync,你应该质疑自己是否做错了什么,也许你应该改用 ViewComponent 或移动逻辑从视图到控制器。

【讨论】:

现在使用 asp.net core 2.1,相同的文档提到 不建议使用同步等效项,因为在某些情况下它们会死锁。 未来版本将不包含同步方法 在 ASP.NET 3.1 中使用Partial 而不是await .. PartialAsync 时会发出警告,因此现在没有两难选择。【参考方案3】:

关于“await Html.PartialAsync” - 此链接可能会对您有所帮助 - http://aspnetwebstack.codeplex.com/workitem/601(也关注 cmets)(至于之前的问题究竟是什么)。

我正在开发一个基于 MVC 6 构建的面向公众的网站,并且“等待 Html.PartialAsync”比“Html.Partial”更快 - 特别是当视图包含大量组件时。

从 Html.PartialAsync 中取出“等待”显然不起作用,并且 Html.PartialAsync 会吐出类型名称(即“System.Threading.Tasks.Task`1[Microsoft.AspNet.Mvc.Rendering.HtmlString ]") 而不是实际的视图。

【讨论】:

【参考方案4】:

只是为了让那些在 asp.net core 时代访问的人保持最新的线程。

目前,根据文档:

Partial 和 RenderPartial 分别是 PartialAsync 和 RenderPartialAsync 的同步等效项。不建议使用同步等效项,因为在某些情况下它们会死锁。 同步方法将在未来版本中移除。

全部内容: Partial views in ASP.NET Core

要了解为什么会出现此问题,您可以查看此 github 条目:

https://github.com/aspnet/Mvc/issues/7083

但长话短说,Partial 的同步版本看起来只是使用 GetResult 调用异步版本,这在某些情况下会导致死锁。

总而言之,没有理由不使用异步版本。有理由不使用同步的。 即使视图中没有巨大的负载和奇特的逻辑,实际上几乎没有机会陷入死锁......但如果你这样做,调试和修复将非常困难。

var result = htmlHelper.RenderPartialAsync(partialViewName, htmlHelper.ViewData.Model, viewData: null);
result.GetAwaiter().GetResult();

【讨论】:

【参考方案5】:

亡灵术。

选项 1:

您的子部分视图在 Razor 标记中有一些异步 await 代码 - 它可能会死锁,因为 MS 代码只调用 GetAwaiter().GetResult(),这是导致死锁的秘诀。

使用PartialAsync

选项 2:

您的局部视图没有任何awaits,但您的模型生成代码有(例如Html.Partial("MyView.cshtml", new MyModel PropertyX = await XyzXyz() ),那么它也可能死锁。

使用PartialAsync

选项 3:

您的部分完全不使用 async/await。

可以使用Html.Partial(同步)

【讨论】:

以上是关于何时在 MVC 6 的视图中使用 @await Html.PartialAsync的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET MVC4 异步控制器 - 为啥要使用?

mvc中的异步页面加载

何时在 MVC4 应用程序中使用 DTO(数据传输对象)? [复制]

SOA 与 MVC - 何时使用

何时使用多个笔尖?

EF6 - 在 Where() 子句中使用 await 关键字