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

时间:2021-02-06 17:00:23

I'm trying to make dynamic menu (stored in DB), that is showing on all web app pages. Using Google I found that it is better to make menu view as a part of Master View (_Layout.cshtml). And because of that, every action method of the controller must contain data with the menu model. To avoid code duplication I found the solution to create a base controller and provide data using its constructor:

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

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

Also, I'm trying to use async/await possibilities and my PageService (menu) is using ToListAsync() to get data from DB. So now I have a problem, that BaseController constructor has an async method:

此外,我正在尝试使用async / await可能性,我的PageService(菜单)使用ToListAsync()从DB获取数据。所以现在我有一个问题,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();
    }
}

I know that this is BAD CODE, but I don't know how to design this situation properly. Maybe there is another better way to create the dynamic menu or another way to get data asynchronously?

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

Also, I found this article, but I don't know if I can apply its solutions because I don't know if I can handle controller instance creation:

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

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

2 个解决方案

#1


6  

Instead of deriving everything from a base controller (which can be a lot of extra work and testing) you can just create a controller called MenuController, create a method called Default and then call it from your Layout:

您可以创建一个名为DefaultControl的方法,然后从您的布局中调用它,而不是从基本控制器(可以进行大量额外工作和测试)中获取所有内容。

[ChildActionOnly]
public Default()
{
  var viewModel = _pageService.GetAllAsync();
  return Partial(viewModel);
}

in your layout:

在你的布局中:

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

This is really the easiest and cleanest solution. The biggest PRO is that you can control the cache for the menu separate from the method calling. There are no good solution for asp.net-mvc (1-5) to run Async code in this fashion. (ActionFilters can't be async and (Render)Partials can't be async. You can still call an async method, it will just run Sync.

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

Render vs Non-Render Performance.

渲染与非渲染性能。

#2


-1  

I changed functionality to call Html.RenderAction in my View, as Erik Philips suggested:

我更改了在我的视图中调用Html.RenderAction的功能,正如Erik Philips建议的那样:

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

and controller:

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);
    }
}

But RenderAction doesn't work with async controller Actions:

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

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

Async PartialView导致“HttpServerUtility.Execute被阻止...”异常

So seems like sync call is the only one possible here.

所以似乎同步调用是唯一可能在这里。

#1


6  

Instead of deriving everything from a base controller (which can be a lot of extra work and testing) you can just create a controller called MenuController, create a method called Default and then call it from your Layout:

您可以创建一个名为DefaultControl的方法,然后从您的布局中调用它,而不是从基本控制器(可以进行大量额外工作和测试)中获取所有内容。

[ChildActionOnly]
public Default()
{
  var viewModel = _pageService.GetAllAsync();
  return Partial(viewModel);
}

in your layout:

在你的布局中:

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

This is really the easiest and cleanest solution. The biggest PRO is that you can control the cache for the menu separate from the method calling. There are no good solution for asp.net-mvc (1-5) to run Async code in this fashion. (ActionFilters can't be async and (Render)Partials can't be async. You can still call an async method, it will just run Sync.

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

Render vs Non-Render Performance.

渲染与非渲染性能。

#2


-1  

I changed functionality to call Html.RenderAction in my View, as Erik Philips suggested:

我更改了在我的视图中调用Html.RenderAction的功能,正如Erik Philips建议的那样:

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

and controller:

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);
    }
}

But RenderAction doesn't work with async controller Actions:

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

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

Async PartialView导致“HttpServerUtility.Execute被阻止...”异常

So seems like sync call is the only one possible here.

所以似乎同步调用是唯一可能在这里。