【问题标题】:ModelFactory in ASP.NET MVC to solve 'RenderPartial' issueASP.NET MVC 中的 ModelFactory 解决“RenderPartial”问题
【发布时间】:2009-03-13 05:46:34
【问题描述】:

ASP.NET MVC 中的“RenderPartial()”方法提供了非常低级别的功能。它不提供,也不试图提供真正的“子控制器”模型*。

我有越来越多的控件通过“RenderPartial()”呈现。它们分为三大类:

1) 直接控制 特定页面的后代 使用该页面的模型

2) 直接控制 特定页面的后代 将该页面的模型与 additional key of some type。 考虑实施 '数据中继器'。

3) 表示不相关的控件 它们出现的页面的功能 在。这可能是任何事情 横幅旋转器,反馈表, 商店定位器,邮件列表注册。 关键是它不在乎 它放在哪个页面上。

由于ViewData 模型的工作方式,每个请求只存在一个模型对象 - 也就是说,子控件需要的任何东西都必须存在于页面模型中。

最终,MVC 团队有望推出真正的“子控制器”模型,但在此之前,我只是在主页面模型中添加子控件也需要的任何内容。

在上述 (3) 的情况下,这意味着我的“ProductModel”模型可能必须包含“MailingListSignup”模型的字段。显然这并不理想,但我已经接受了与当前框架的最佳折衷方案 - 并且最不可能“关闭任何门”到未来的子控制器模型。

控制器应该负责获取模型的数据,因为模型实际上应该只是一个不知道从哪里获取数据的愚蠢数据结构。但我不希望控制器必须在几个不同的地方创建模型。

我已经开始做的是创建一个工厂来为我创建模型。这个工厂被控制器调用(模型不知道工厂)。

public static class JoinMailingListModelFactory {

        public static JoinMailingListModel CreateJoinMailingListModel() {

            return new JoinMailingListModel()
            {
                MailingLists = MailingListCache.GetPartnerMailingLists();
            };
        }
    }   

所以我的实际问题是其他有同样问题的人实际上是如何创建模型的。未来与新 MVC 功能兼容的最佳方法是什么?


  • 注意:RenderAction() 存在一些问题,我不会在这里讨论——尤其是它只存在于 MVCContrib 中,不会出现在 ASP.NET-MVC 的 RTM 版本中。其他问题导致sufficent problems 我选择不使用它。所以让我们暂时假设只有 RenderPartial() 存在 - 或者至少那是我决定使用的。

【问题讨论】:

    标签: asp.net-mvc renderpartial subcontroller


    【解决方案1】:

    不要将MailingListSignup 之类的东西添加为ProductModel 的属性,而是将两者封装在一个类似ProductViewModel 的类中,如下所示:

    public class ProductViewModel() {
        public ProductModel productModel;
        public MailingListSignup signup;
    }
    

    然后让您的视图被强类型化为ProductViewModel 类。您可以通过调用Model.productModel 访问ProductModel,也可以使用Model.signup 访问注册类。

    这是对 Fowler 的“演示模型”(http://martinfowler.com/eaaDev/PresentationModel.html) 的松散解释,但我已经看到一些 Microsoft 开发人员使用它,例如 Rob Conery 和 Stephen Walther。

    【讨论】:

    • 我经常把 asp.net MVC 称为 MVVC :)
    • 这通常是我采用的方法,但我仍然闻起来有点滑稽。我不喜欢外部视图提供了它不需要的数据这一事实。我已经开始使用 Html.RenderAction() 作为真正的子控制器的代理,它似乎比 Html.RenderPartial() 更好,但它仍然有些弱。
    • @James,我按照你的方式做,productModel 和 SignUpModel 是实际的数据模型。但我发现自己失去了验证注释的好处。我使用 Fluent Validation 在域级别验证事物,似乎还可以。
    【解决方案2】:

    我在这种情况下看到的一种方法是使用动作过滤器来填充局部视图的数据 - 即子类 ActionFilterAttribute。在OnActionExecuting 中,将数据添加到 ViewData。然后,您只需使用过滤器装饰 使用该局部视图的不同操作。

    【讨论】:

    • 我想的越多,我就越不喜欢这个想法。然后我想得越多,我就越喜欢它。主要是因为任何人都可以更轻松地向页面添加控件 - 只需添加属性和 RenderPartial 即可。
    • 这听起来像很多想法;-p
    • 最终在我的应用程序的几个地方使用了这种方法。使将某些东西应用于全球模型变得更加清洁。我将它用于与母版页相对应的“基本模型”以及站点中不同区域的“区域模型”。相当灵活,虽然我并不完全喜欢将东西注入我的模型中,但它工作得很好
    【解决方案3】:

    我使用了一个 RenderPartial 重载,让您可以指定新的 ViewData 和 Model:

    RenderPartial code

    如果你看之前链接的MVC源码,还有下面的(找RenderPartialInternal方法):

    RenderPartialInternal code

    您可以看到,如果基本上复制您传递的视图数据,则创建一个新字典并设置要在控件中使用的模型。所以页面可以有一个Model,但随后将不同的Model传递给子控件。

    如果子控件不直接从主视图模型中引用,您可以使用 Marc Gravell 提到的技巧来添加您的自定义逻辑。

    【讨论】:

      【解决方案4】:

      我尝试的一种方法是使用带有接口的强类型分部视图。在大多数情况下,聚合的 ViewModel 是更好的方法,但我仍然想分享这一点。

      <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IMailingListSignup>" %>
      

      Viewmodel 实现接口

       public class ProductViewModel:IMailingListSignup
      

      这根本不完美,但解决了一些问题:您仍然可以轻松地将属性从路由映射到模型。如果您可以将路由参数映射到 MailingListSignup 的属性,我不确定。

      您仍然有填充模型的问题。如果还不算晚,我更喜欢在 OnActionExecuted 中进行。我看不出如何在 OnActionExecuting 中填充模型。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多