构造函数控制器中的 MVC 异步方法

Posted

技术标签:

【中文标题】构造函数控制器中的 MVC 异步方法【英文标题】:MVC async method in constructor controller 【发布时间】:2018-06-05 07:49:13 【问题描述】:

我正在尝试制作动态菜单(存储在数据库中),该菜单显示在所有 Web 应用程序页面上。 使用 Google 我发现将菜单视图作为主视图 (_Layout.cshtml) 的一部分会更好。正因为如此,控制器的每个动作方法都必须包含菜单模型的数据。为了避免代码重复,我找到了创建基本控制器并使用其构造函数提供数据的解决方案:

https://docs.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/views/passing-data-to-view-master-pages-cs

另外,我正在尝试使用异步/等待可能性,而我的 PageService(菜单)正在使用 ToListAsync() 从数据库中获取数据。所以现在我有一个问题,BaseController 构造函数有一个异步方法:

public class BaseController : AsyncController, IBaseController

    private readonly IPageService _pageService;

    public BaseController(IPageService pageService)
    
        _pageService = pageService;
        SetBaseViewModelAsync();
    

    private async Task SetBaseViewModelAsync()
    
        ViewData["Pages"] = await _pageService.GetAllAsync();
    

我知道这是BAD CODE,但我不知道如何正确设计这种情况。也许有另一种更好的方法来创建动态菜单或另一种异步获取数据的方法?

另外,我找到了这篇文章,但我不知道我是否可以应用它的解决方案,因为我不知道我是否可以处理控制器实例的创建:

http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html

【问题讨论】:

那是糟糕的设计,你是对的。您应该改用中间件或操作过滤器 感谢您的回复!您能否提供一些具有适当设计的文章的链接?因为我搜索了大约 1-2 小时来找到如何实现动态菜单。 这听起来很适合Child Action。另请参阅此问题***.com/questions/21948909/… @Jasen,RenderAction 不能调用异步操作 【参考方案1】:

您可以创建一个名为MenuController 的控制器,创建一个名为Default 的方法,然后从您的布局中调用它,而不是从基本控制器派生所有内容(这可能需要大量额外的工作和测试):

[ChildActionOnly]
public Default()

  var viewModel = _pageService.GetAllAsync();
  return Partial(viewModel);

在你的布局中:

@Html.RenderAction("Default", "Menu");

这确实是最简单、最干净的解决方案。最大的优点是您可以控制菜单的缓存与方法调用分开。 asp.net-mvc (1-5) 没有很好的解决方案来以这种方式运行异步代码。 (ActionFilters can't be async 和 (Render)Partials 不能异步。你仍然可以调用异步方法,它只会运行同步。

Render vs Non-Render Performance.

【讨论】:

感谢您的回复!我是 MVC 的新手,所以我将阅读 RenderAction 并尝试您的解决方案。此外,这里的缓存可能性很大! 应用了您的解决方案,但它有一些细微差别:子动作不能异步:***.com/questions/24072720/… 所以我决定不将动作设为 ChildAction。有必要让它只有孩子吗?您能否也修改您的答案,以便接受是正确的?因为现在您的示例在 ChildAction 中有异步代码 异步代码有 async/await 关键字。我的不使用这些关键字。它应该在编译时出现警告。否则你可以call async code within a sync method。 在同步方法中调用异步代码是个坏主意。 IMO 我会更好地删除 ChildAction 属性并保持代码异步 我错了,那只是我的缓存,它返回了正确的结果给我。我删除了缓存,我得到了与这里相同的错误:***.com/questions/24072720/… 所以我不能将 RenderAction 与异步操作一起使用...然后我将调用同步方法。似乎不可能意识到我的异步代码问题。【参考方案2】:

按照 Erik Philips 的建议,我将功能更改为在视图中调用 Html.RenderAction:

@
    Html.RenderAction("Index", "Pages");

和控制器:

public class PagesController : AsyncController, IPagesController

    private readonly IPagesService _pagesService;

    public PagesController(IPagesService pagesService)
    
        _pagesService = pagesService;
    

    [HttpGet]
    [Route("")]
    public async Task<ActionResult> IndexAsync()
    
        var viewModel = await _pagesService.GetAllAsync();
        return PartialView("MenuPartial", viewModel);
    

但 RenderAction 不适用于异步控制器操作:

Async PartialView causes "HttpServerUtility.Execute blocked..." exception

看来同步调用是这里唯一可能的方法。

【讨论】:

以上是关于构造函数控制器中的 MVC 异步方法的主要内容,如果未能解决你的问题,请参考以下文章

ts ES5/ES3 中的异步函数或方法需要 'Promise' 构造函数

在 C# 中的类构造函数中调用异步方法 [重复]

用于 ASP.NET MVC 的没有 DI 容器的控制器的构造函数参数

在 ASP.NET MVC Core 控制器的构造函数中设置 ViewBag 属性

ASP.NET MVC 模型验证不适用于控制器构造函数之一

MVC5 和 Ninject:无参数构造函数错误