如何在ASP.NET MVC中使用基础控制器创建强类型主页?

20

根据NerdDinners示例,我有兴趣创建一个强类型的主页。为了实现这一点,我使用一个基础控制器来检索主页面的数据。所有其他控制器都继承自此类。同样,我对主页面和任何其他视图都有ViewModels。视图ViewModel类继承自主页面的ViewModel

问题

子控制器如何确保将主页面的数据传递给视图,而不设置其与主页面本身相关的ViewModel属性?

我的主页将显示许多按钮,这些按钮在XML文件中确定,因此我正在填充Buttons类。

MasterPage ViewModel 代码片段

using System.Collections.Generic;

namespace Site1.Models
{
    public class MasterViewModel
    {
        public List<Button> Buttons{set; get;}
    }
}

查看 ViewModel

namespace Site1.Models
{
    public class View1ViewModel : MasterViewModel
    {
        public SomeDataClass SomeData { get; set; }
    }
}

基础控制器

using System.Collections.Generic;
using System.Web.Mvc;
using Site1.Models;

namespace Site1.Controllers
{
    public abstract class BaseController : Controller
    {
        protected MasterViewModel model = new MasterViewModel();

        public BaseController()
        {
            model.Buttons = new List<Button>();
            //populate the button classes (doesn't matter how)
            PopulateButtons(model.Buttons);
        }
    }
}

视图控制器:

using System.Web.Mvc;

namespace Site1.Controllers
{
    public class View1Controller : BaseController
    {
        public ActionResult Index()
        {
            Models.View1ViewModel viewModel = new Models.View1ViewModel();
            SomeDataClass viewData = new SomeDataClass()
            //populate data class (doesn't matter how)
            PopulateDataClass(viewData);
            viewModel.SomeData = viewData;
            //I WANT TO ELIMINATE THE FOLLOWING LINE!
            viewModel.Buttons = model.Buttons;
            return View("Index", viewModel);
        }
    }
}

主页面继承System.Web.Mvc.ViewMasterPage<Site1.Models.MasterViewModel>

视图继承System.Web.Mvc.ViewMasterPage<Site1.Models.View1ViewModel>


1
将解决方案从问题中移动到它所属的已接受解决方案中。这样干净地分离了 Stack Overflow 使用的问题/答案模型。 - George Stocker
2个回答

17

你可以创建一个在操作执行后执行的过滤器,该过滤器查找该类型的模型并设置属性,可以通过调用基控制器函数来实现。然后将过滤器放在基类上,所有操作都会自动看到它。

操作过滤器属性获取控制器的ViewModel,并将其传递给控制器的SetModel函数:

using System.Web.Mvc;
using Site1.Controllers;

namespace Site1.Models
{
    public class MasterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            base.OnActionExecuted(filterContext);

            MasterViewModel viewModel = (MasterViewModel)((ViewResultBase)filterContext.Result).ViewData.Model;

            BaseController controller = (BaseController)filterContext.Controller;
            controller.SetModel(viewModel);
        }
    }
}

这个函数被添加到了BaseController中:

public void SetModel(MasterViewModel childViewModel)
{
    childViewModel.Buttons = model.Buttons;
}

2
在我尝试之前,这似乎是一个出乎意料的复杂解决方案,对于一个非常常见的情况来说。或者人们不会为他们的主页面强制类型转换吗? - darasd
我没有看到过很多强类型的母版页。然而,您将面临使用ViewDataDictionary设置公共属性的相同问题。不过,事实证明,创建一个操作筛选器真的非常容易。我认为您高估了它的复杂性。 - Craig Stuntz
啊,我看到你已经更新了这个问题的实现。很简单,不是吗! - Craig Stuntz
2
谢谢您。非常棒。我需要在主页面上以不同的方式呈现一些内容,这个提示帮助了我。 - Scott Arrington

6
不妨在Controller.OnActionExecuted中覆盖代码来初始化,而不是创建属性。这种方法似乎更简单一些。

这是来自 MSDN 的建议:“如果此方法在派生的 Controller 类中被重写,那么它将为类中的每个操作方法调用。为了更灵活,请从 ActionFilterAttribute 派生一个类,并在派生的 ActionFilterAttribute 类中重写此方法。” - Aaron Bennett
这是 Controller.OnActionExecutedMSDN文档链接,其中包含Aaron在上面评论中提到的引用。 - Rich C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接