我正在尝试制作动态菜单(存储在数据库中),该菜单显示在所有网络应用页面上。 使用Google我发现将菜单视图作为Master View(_Layout.cshtml)的一部分更好。因此,控制器的每个操作方法都必须包含菜单模型的数据。为避免代码重复,我找到了创建基本控制器并使用其构造函数提供数据的解决方案:
此外,我尝试使用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();
}
}
我知道这是 BAD CODE ,但我不知道如何正确设计这种情况。也许有另一种更好的方法来创建动态菜单或另一种异步获取数据的方式?
此外,我发现了这篇文章,但我不知道我是否可以应用其解决方案,因为我不知道我是否可以处理控制器实例:
http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html
答案 0 :(得分:6)
您可以创建一个名为MenuController
的控制器,创建一个名为Default
的方法,然后从中调用它,而不是从基本控制器(可以进行大量额外工作和测试)中获取所有内容。你的布局:
[ChildActionOnly]
public Default()
{
var viewModel = _pageService.GetAllAsync();
return Partial(viewModel);
}
布局中的:
@{Html.RenderAction("Default", "Menu");}
这是最简单,最干净的解决方案。最大的PRO是你可以控制菜单的缓存,与方法调用分开。 asp.net-mvc(1-5)没有很好的解决方案以这种方式运行异步代码。 (ActionFilters can't be async和(渲染)部分不能是异步的。您仍然可以调用异步方法,它只会运行同步。
答案 1 :(得分:-1)
我更改了在我的视图中调用Html.RenderAction的功能,正如Erik Philips建议的那样:
@{
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
所以似乎同步调用是唯一可能的。