【问题标题】:Multiple models sent to a single view instance多个模型发送到单个视图实例
【发布时间】:2011-01-01 12:09:53
【问题描述】:

我的术语可能离这里很远,但基本上我试图将多个数据模型传递给一个视图。为了帮助把问题放在上下文中,举个例子:

假设我正在写博客。当我登录时,我希望主屏幕显示所有未批准的新 cmets 列表、最近注册用户列表和最近提交的博客文章列表。

我见过的大多数讨论都建议对视图页面进行强类型化,以便可以使用类似“return View(RecentComments)”之类的内容来调用它并遍历视图中的 cmets,或者将数据模型转换为“var NewUsers” = (MembershipUserCollection) ViewData.Model"。我理想地追求的是传递多个模型同时仍保持适当的逻辑分离的“正确”或至少“足够正确”的方式。

【问题讨论】:

  • sigh 事后看来这很明显......

标签: asp.net asp.net-mvc model


【解决方案1】:

一种方法是创建一个封装两个模型数据的新类型:

public class MyBigViewData {
    public SubData1 SubData1 { get; set; }
    public SubData2 SubData2 { get; set; }
}

public class SubData1 {
    ... more properties here ...
}

public class SubData2 {
    ... more properties here ...
}

另一种方式是将“主”模型数据存储为强类型数据,并将视图数据中的其他数据存储为字典项:

ViewData["username"] = "joe"; // "other" data
ViewData["something"] = "whatever"; // "other" data
ViewData["subdata1"] = new SubData1(...);
return View(myRealModelData);

第二种方法的优点是您根本不需要做任何特别的事情:它开箱即用。

【讨论】:

  • 我会做最简单的事情,可以在这里工作 - 所以将数据存储在 ViewData 集合中。容易。
  • 这种方法的问题在于,您最终可能会为每个视图添加大量视图模型。有人认为这应该是这样,在一个完美的世界里我同意,但这种程度的定制通常超出了要求,实际上会使项目更难理解。一种更快速但可能存在风险的方法是将我在下面的答案与您的域实体一起使用。在某些环境中,这是不可接受的。例如,如果您将视图开发外包,您可能不想让您的实体在视图中可见或可调用。
【解决方案2】:

我过去所做的是编写一个类,其中包含我在视图中需要的两个类的实例。

public class City{
public Mall TheMall;
public School TheSchool;
}

然后您的视图将被强类型化为 City,您将使用 Model.TheMall.Property 和 Model.TheSchool.Property 来访问您需要的内容

编辑

这是其他海报通过创建一个具有两个对象作为字段/属性的对象的示例

【讨论】:

    【解决方案3】:

    遗憾的是,实现传递多个对象的唯一方法是创建一个对象,将两个对象都作为字段/属性,或者使用弱类型数组。

    【讨论】:

      【解决方案4】:

      将多个模型传递给视图的方法是创建我们称之为表单视图模型的模型,其中包含您的其他模型。

      然后在您的视图中,您可以将表单视图模型中包含的各个模型传递给负责在所述模型中呈现数据的部分视图。

      有道理吗?

      编辑

      顺便说一句:表单视图模型只是一个类。这不是我的回答可能暗示的特殊类型。

      【讨论】:

      • 这个词是ViewModel 他称它为FormViewModel,因为用户填写了Form。如果您不填写表单,那么它只是一个ViewModel,它允许传递强类型对象而不是魔术字符串。
      【解决方案5】:

      除了我的其他答案之外,另一种方法是不在页面指令中强类型化视图和母版页,而是使用来自MVC Contrib 的基于通用类型的 ViewData 扩展。这些扩展基本上使用完全限定的类型名称作为 ViewData 字典键。实际上,打字的好处与强类型页面方法相同,就所需的视图模型类的数量而言,类开销更少。然后在你的行动中做

      ViewData.Add<Car>(car);
      ViewData.Add<LayoutAData>(layoutAData);
      

      在你所做的视图中

      <%= ViewData.Get<Car>().Color %>
      

      在你的母版页中

      <%= ViewData.Get<LayoutAData>().Username %>
      

      您可以在视图中内联缓存这些 Get 调用,以降低多次转换的成本。

      【讨论】:

        【解决方案6】:

        在开发了一个大型 ASP.NET MVC 应用程序后,我发现在最小化运行时强制转换的同时最高效的方法是使用泛型来模拟视图的嵌套结构。本质上,视图有自己的数据类型。通常,这些要么是域对象,要么是包含元数据的域对象的集合。这些类型的通用版本可用于所有可能的母版页,采用类型参数来定义与母版页相关的数据。

        public class Car {
          // can be used as a model
        } 
        
        public class CarCollection: Collection<Car> {
          public BodyTypes BodyType {get;set;}
          public Colors Color {get;set;}
          // can also be used as a model
        }
        
        public interface ILayoutModel<TLayout> {
          TLayout LayoutModel {get;set;}
        }
        
        public class CarView<TLayout>: Car, ILayoutModel<TLayout>  {
          // model that can be used with strongly-typed master page
        }
        
        public class CarCollection<TLayout> : CarCollection, ILayoutModel<TLayout> {
          // model that can be used with strongly-typed master page
        }
        
        public class LayoutAData {
          // model for LayoutA.master
        }
        
        public class LayoutBData {
          // model for LayoutB.master
        }
        

        也可以颠倒通用性,但由于视图决定布局,在我看来,视图数​​据应该支配布局数据。 LayoutA.master 将派生自 ViewMasterPage&lt;ILayoutModel&lt;LayoutAData&gt;&gt;,而 LayoutB.master 将派生自 ViewMasterPage&lt;ILayoutModel&lt;LayoutBData&gt;&gt;。这以一致、强类型和灵活的方式保持视图数据和布局数据分开。

        【讨论】:

          【解决方案7】:

          创建一个可以封装其他对象的对象是最好的方法。否则你会在控制器和视图中遇到一堆丑陋的 ViewData 标签。

          【讨论】:

            【解决方案8】:

            似乎没有人提到的一件事是,在最初的提问者示例中,从处理自己的功能区域的一系列子操作创建视图而不是制作大型视图模型可能是有意义的。这样可以重复使用未批准的消息等组件。

            这也提供了更好的封装。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2018-08-17
              • 1970-01-01
              • 2014-08-15
              • 1970-01-01
              • 2023-02-14
              • 2020-02-19
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多