Asp.net mvc 视图和布局的Asp.net MVC模型

Asp.net mvc 视图和布局的Asp.net MVC模型,asp.net-mvc,Asp.net Mvc,我一直在试图找到一个好方法来处理我们的Asp.NETMVC网站的模型,当所有页面都有公共属性时。这些属性将显示在布局(母版页)中。我正在使用一个“BaseModel”类来保存这些属性,我的布局使用这个BaseModel作为它的模型 其他每个模型都继承自该BaseModel,并且每个模型都具有与其表示的视图相关的特定属性。正如您可能已经猜到的,我的模型实际上是视图模型,即使这在这里不太相关 我尝试了不同的方法来初始化BaseModel值 在每个视图中“手动” 有一个基本控制器,它有一个初始化虚拟方

我一直在试图找到一个好方法来处理我们的Asp.NETMVC网站的模型,当所有页面都有公共属性时。这些属性将显示在布局(母版页)中。我正在使用一个“BaseModel”类来保存这些属性,我的布局使用这个BaseModel作为它的模型

其他每个模型都继承自该BaseModel,并且每个模型都具有与其表示的视图相关的特定属性。正如您可能已经猜到的,我的模型实际上是视图模型,即使这在这里不太相关

我尝试了不同的方法来初始化BaseModel值

  • 在每个视图中“手动”
  • 有一个基本控制器,它有一个初始化虚拟方法来执行(因此特定的控制器可以实现特定的公共行为,例如)
  • 具有重写OnActionExecuting以调用Initialize方法的基本控件
  • 使用助手类在控制器外部执行此操作
  • 使用模型工厂
  • 但这些都没有真正吸引我:

  • 对我来说似乎很明显,但DRY是一个足以证明这一点的理由(实际上我根本没有尝试过那个解决方案,我只是把它放在最后一点上,以便能够循环到那个点上)
  • 我不喜欢这个方法,因为这意味着无论何时添加新控制器,您都需要知道它必须从BaseController继承,并且需要调用Initialize方法,更不用说如果您的控制器重写了BaseController,则无论如何都要调用BaseController来维护值
  • 见下一点
  • 三,。是同一主题的变体,但这对第二个解决方案的问题没有真正的帮助
  • 到目前为止我最喜欢的,但是现在我必须传递一些变量来设置这些值。我喜欢它的依赖倒置。但是如果我想从会话中提供值,我需要显式地传递它们,例如,然后我回到原点,因为我必须手动提供它们(作为引用或通过任何类型的接口)
  • 当然,(几乎)所有这些解决方案都有效,但我正在寻找更好的方法

    在输入这个问题时,我发现了一条新的路径,这可能也可以,但实现也可能很快成为负担,因为我们可以有几十个视图和控制器

    我很乐意接受任何严肃的建议/暗示/建议/模式/建议

    更新

    多亏了@EBarr,我想出了另一个解决方案,使用ActionFilterAttribute(不是生产代码,在5分钟内完成):

    那么,给它打电话真的很简单:

    [Model(typeof(HomeModel))]
    public ActionResult Index()
    {
        var homeModel = TempData["model"] as HomeModel;
    
        // Add View Specific stuff
    
        return View(homeModel);
    }
    
    它给了我世界上最好的东西。唯一的缺点是找到一种正确的方法将模型传递回操作

    这里是使用TimDATA对象完成的,但我也考虑更新ActoMealth.

    中可以找到的模型。
    对于这一点,我仍然在接受任何严肃的建议/提示/建议/模式/建议,或者前面的几点。

    我经历的过程与我跳入MVC的过程几乎完全相同。你是对的,没有一个解决方案感觉那么好

    最后,我使用了一系列基本模型。出于各种原因,我有几种不同类型的基本模型,但逻辑应该应用于单个基本类型。然后,我的大多数视图模型继承自其中一个基础。然后,根据需要/时间,我在
    ActionExecuting
    onactionexecutive
    中填充模型的基本部分

    我的一段代码应该可以让流程变得清晰:

    if (filterContext.ActionParameters.ContainsKey("model")) {
       var tempModel = (System.Object)filterContext.ActionParameters["model"];
    
       if (typeof(BaseModel_SuperLight).IsAssignableFrom(tempModel.GetType())) {
           //do stuff required by light weight model
       }
    
       if (typeof(BaseModel_RegularWeight).IsAssignableFrom(tempModel.GetType())) {
          //do more costly stuff for regular weight model here
       }
    }
    

    最后,我的模式感觉不太令人满意。然而,它是实用的、灵活的,并且易于实现不同级别的继承。我还能够在控制器执行之前或之后注入执行,这在我的案例中非常重要。希望这能有所帮助。

    让我@EBarr使用动作过滤器的想法实际上是可行的,但最终感觉是错误的,因为在不通过viewbag或httpcontext项或类似内容的情况下,没有干净的方法检索模型。此外,它还强制要求用它的模型装饰每个动作。这也使得回发更加难以处理。我仍然相信这种解决方案有其优点,在某些特定情况下可能会有用

    所以我回到原点,开始更多地研究这个话题。我来到了下面。首先,问题有两个方面

  • 初始化视图的数据
  • 呈现数据
  • 在寻找更多想法的同时,我意识到我没有从正确的角度看待问题。我是从一个“控制器”的视角来看它的,而模型的最终客户机是视图。我还被提醒布局/母版页不是视图,不应该有与之关联的模型。这种洞察力让我走上了一条适合我的道路。因为这意味着布局的每个“动态”部分都应该在它之外处理。当然,由于部分的灵活性,它们似乎非常适合这样做

    在我制作的测试溶液中,我有(只有)4个不同的部分,有些是强制性的,有些不是。分区的问题在于,您需要在每个页面上添加分区,这很快就会成为更新/修改的难题。为了解决这个问题,我尝试了以下方法:

    public interface IViewModel
    {
        KeyValuePair<string, PartialViewData>[] Sections { get; }
    }
    
    public class PartialViewData
    {
        public string PartialViewName { get; set; }
        public object PartialViewModel { get; set; }
        public ViewDataDictionary ViewData { get; set; }
    }   
    
    实际的解决方案有更多的代码,我在这里试图简化以获得想法。它仍然有点粗糙,需要抛光/错误处理,但有了它,我可以在我的操作中定义,这些部分将是什么,它们将使用什么模型等等。它可以很容易地进行测试,设置DI不应该是一个问题

    我仍然必须在每个视图中复制@section行,这似乎有点痛苦(特别是因为我们不能将这些部分放在局部视图中)


    我正在调查,看看这是否能取代这些章节。

    感谢您的反馈。虽然它与我想要的不完全匹配(您的解决方案是3的不同实现)
    public interface IViewModel
    {
        KeyValuePair<string, PartialViewData>[] Sections { get; }
    }
    
    public class PartialViewData
    {
        public string PartialViewName { get; set; }
        public object PartialViewModel { get; set; }
        public ViewDataDictionary ViewData { get; set; }
    }   
    
    public class HomeViewModel : IViewModel
    {
        public Article[] Articles { get; set; }             // Article is just a dummy class 
        public string QuickContactMessage { get; set; }     // just here to try things
    
        public HomeViewModel() { Articles = new Article[0]; }
    
        private Dictionary<string, PartialViewData> _Sections = new Dictionary<string, PartialViewData>();
        public KeyValuePair<string, PartialViewData>[] Sections
        {
            get { return _Sections.ToArray(); }
            set { _Sections = value.ToDictionary(item => item.Key, item => item.Value); }
        }
    }
    
    public ActionResult Index()
    {
        var hvm = ModelFactory.Get<HomeViewModel>(); // Does not much, basicaly a new HomeViewModel();
    
        hvm.Sections = LayoutHelper.GetCommonSections().ToArray(); // more on this just after
        hvm.Articles = ArticlesProvider.GetArticles(); // ArticlesProvider could support DI
    
        return View(hvm);
    }
    
    public class DefaultLayoutHelper
    {
        private Controller Controller;
        public DefaultLayoutHelper(Controller controller) { Controller = controller; }
    
        public Dictionary<string, PartialViewData> GetCommonSections(QuickContactModel quickContactModel = null)
        {
            var sections = new Dictionary<string, PartialViewData>();
            // those calls were made in methods in the solution, I removed it to reduce the length of the answer
            sections.Add("header",
                         Controller.UserLoggedIn() // simple extension that check if there is a user logged in
                         ? new PartialViewData { PartialViewName = "HeaderLoggedIn", PartialViewModel = new HeaderLoggedInViewModel { Username = "Bishop" } } 
                         : new PartialViewData { PartialViewName = "HeaderNotLoggedIn", PartialViewModel = new HeaderLoggedOutViewModel() });
            sections.Add("quotes", new PartialViewData { PartialViewName = "Quotes" });
            sections.Add("quickcontact", new PartialViewData { PartialViewName = "QuickContactForm", PartialViewModel = model ?? new QuickContactModel() });
            return sections;
        }
    }
    
    @section       quotes { @{ Html.RenderPartial(Model.Sections.FirstOrDefault(s => s.Key == "quotes").Value); } }
    @section        login { @{ Html.RenderPartial(Model.Sections.FirstOrDefault(s => s.Key == "header").Value); } }
    @section       footer { @{ Html.RenderPartial(Model.Sections.FirstOrDefault(s => s.Key == "footer").Value); } }