如何在每个控制器动作中使用异步ViewModel?

时间:2015-12-01 15:32:24

标签: c# asp.net-mvc asynchronous viewmodel

我是asp.net mvc(5)的新手,我在网站上遇到了一个问题。

基本上,所有aspnet_users都通过user链接到我的特定guid表。

我有一个BaseController课程,其中包含UnitOfWorkViewModelBase

public class BaseController : Controller
{
    protected UnitOfWork UnitOfWork { get; private set; }
    public ViewModelBase ViewModel { get; set; }
}

(摘录)ViewModelBase类,包含布局页面中所需的信息:

public class ViewModelBase
{
    public User User { get; set; }
    public bool IsAuthentified { get; set; }
    public bool Loaded { get; set; }
}

使用它的布局:

@model Project.ViewModels.ViewModelBase
<html>
    <body>
        Some very cool layout stuff related to the User in the ViewModelBase
    </body>
</html>

以下是与HomeController

一起使用的示例
public class HomeController : BaseController
{
    private new HomeViewModel ViewModel
    {
        get { return (HomeViewModel)base.ViewModel; }
    }

    public HomeController()
    {
        ViewModel = new HomeViewModel();
    }

    public ActionResult Index()
    {
        // I need to get the MembershipUser here to retrieve the related User and set it in the ViewModel
        return View(ViewModel);
    }
}

HomeViewModel

public class HomeViewModel : ViewModelBase
{
    public string HomeSpecificProperty { get; set; }
}

观点:

@model Project.ViewModels.HomeViewModel

@{
    ViewBag.Title = "Home";
    Layout = "~/Views/Shared/Layout.cshtml";
}

Welcome @Model.User.UserName !
<br />
@Model.HomeSpecificProperty

这是我的问题。事情是所有页面都有一个继承自ViewModelBase的视图模型,因为它在布局中使用,但我不知道在哪里或如何在其中设置User属性。 由于Membership用于检索User Action,因此我无法在ViewModelBase构造函数中执行此操作。

因此,我在BaseController中添加了此代码,该代码在第一个ViewModelBase上设置get属性:

private ViewModelBase viewModel;

protected ViewModelBase ViewModel
{
    get
    {
        if (!viewModel.Loaded)
            LoadBaseContext();

        return viewModel;
    }
    set { viewModel = value; }
}

private void LoadBaseContext()
{
    viewModel.IsAuthentified = HttpContext.User.Identity.IsAuthenticated;

    if (viewModel.IsAuthentified)
    {
        MembershipUser user = Membership.GetUser();
        viewModel.Player = UnitOfWork.UserRepo.Get((Guid)user.ProviderUserKey);
    }

    viewModel.Loaded = true;
}

可能不是很漂亮,但有效。但是,由于其中存在一些数据库(特别是获取User),我认为我应该放置LoadBaseContext函数async。 我尝试过,因为所有操作都使用ViewModelBase我也将每个操作都设置为异步。但是,@Html.ActionLink已经不再有效,因为所谓的操作是async

最后,我的问题是:这个ViewModelBaseUser属性是正确的方法吗?如果是,那么LoadBaseContext应该是异步的吗?然后,如何让它与行动一起工作?

非常感谢。

更新

布局摘录:

@if (!Model.IsAuthentified)
{
    @Html.Action("Index", "Authentification", null) // display login partial view
}
else
{
    @Html.Action("Index", "UserInfos", null) // display userinfos partial view
}

认证控制器索引的行动:

public ActionResult Index()
{
    ViewModel = new AuthentificationViewModel();
    // loads the ViewModelBase properties here
    return PartialView("~/Views/Shared/Partials/Login.cshtml", ViewModel);
}

1 个答案:

答案 0 :(得分:3)

如果您在所有视图中都有所需的内容,则可以确保每个视图都采用包含或扩展该核心数据集的视图模型,您可以简单地放置Ok, passed 10000 tests.

中的数据

要执行后者,您可以覆盖基本控制器中的ViewBag,并在那里设置OnActionExecuting内容。

ViewBag

如果您需要protected override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); this.ViewBag["IsAuthenticated"] = this.Request.IsAuthenticated; // ... } 行为,那么您可以根据自己的需要调整this post

要在不覆盖async的情况下执行此操作,您可以在控制器基类中放置OnActionExecuting方法,以设置async中的数据:

ViewBag

...然后只需从您的每个控制器操作中调用它:

protected async virtual Task PopulateViewBag()
{
    this.ViewBag["foo"] = await MyAsyncMethod();

    // ...
}
但是,这会有点单调乏味。

最后一句话:取决于您希望拥有多少用户等。一旦获取用户信息可能更容易,然后将其缓存(例如, public async Task<ActionResult> MyAction() { await this.PopulateViewBag(); // ... more code } ),而不是在每次请求期间反复获取它。据推测,大多数用户信息都不会在请求之间发生变化......