65.9K
CodeProject 正在变化。 阅读更多。
Home

MVC 中绑定多个模型的 10 种方法

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (74投票s)

2016 年 6 月 26 日

CPOL

8分钟阅读

viewsIcon

292465

MVC 中绑定多个模型的 10 种方法

引言

在 MVC 中,我们不能在一个视图中使用多个模型标签。但很多时候,我们需要从控制器将多个模型传递到视图,或者我们想在一个视图中展示来自多个模型的数据。例如,我们想在一个视图中同时展示一篇博文和该博文的评论列表,或者假设我们想在一个视图中同时展示一个分类及其相关的产品列表。因此,在这些情况下,我们需要将多个模型传递给一个视图。这个问题有很多解决方案。所以在本文中,我们将探讨绑定多个模型到视图的不同方法。

问题陈述

我们有两个模型,分别命名为 BlogComment,我们想创建一个视图来展示博文标题、博文描述以及与该博文相关的评论列表。

绑定两个模型时遇到的问题

如果我们尝试像下面这样在一个视图中绑定 BlogComment 模型,那么在运行时,我们会收到一个异常,提示“无法在一个视图中绑定多个模型”。

异常信息

所以,在 MVC 中,我们需要一些方法来实现这一点,幸运的是,我将提供 10 种实现相同目标的方法。我不是专家,我确信我会在多次尝试中犯错或违背最佳实践。我不会讨论不同方法的优缺点。我的目的是展示可以用来解决这个问题的不同方法。

实现目标的各种方法

我们将通过以下 10 种方法来绑定多个模型到单个视图:

  1. ViewModel
  2. View Bag
  3. View Data
  4. Temp Data
  5. Session
  6. 动态
  7. 元组
  8. Render Action
  9. JSON
  10. 导航属性

我们将通过一个示例(即上述问题陈述的解决方案)来研究所有这些方法。在我们的问题陈述中,我们想在一个视图中显示一篇博文和评论列表。

以下是本文的主要参与者:

  1. Blog 模型
  2. Comment 模型
  3. GetBlog():在此方法中,我将创建用于 Blog 的假数据。
  4. GetComments():在此方法中,我将创建用于 Comment 的假数据。
  5. 一个 Controller(我将称其为 BlogCommentController
  6. 视图

我创建了一个名为 MultipleModels 的项目,现在我将在开始不同的方法之前创建所有必需的元素。

  1. Blog 模型:创建一个名为 BlogModel 的模型。右键单击“Models”文件夹并添加一个名为 BlogModel 的类。我在 Models 文件夹中创建了以下 BlogModel 类。
       public class BlogModel
        {
            public int BlogID { get; set; }
            public string BlogTitle { get; set; }
            public string BlogDescription { get; set; }
            public DateTime CreatedDate { get; set; }
        }
  2. Comment 模型:右键单击“Models”文件夹并添加另一个名为 CommentModel 的类,如下所示。
      public class CommentModel
        {
            public int CommentID { get; set; }
            public string Comment { get; set; }
            public string CommentedBy { get; set; }
            public int BlogID { get; set; }
        }

1. 视图模型 (ViewModel)

ViewModel 是一个类,它包含在视图中表示的属性,或者表示您想在视图/页面上显示的数据。

假设我们想创建一个 View 来显示一篇博文和评论列表,那么我们将创建我们的 ViewModelBlogCommentViewModel.cs)。在您的项目中创建一个名为 ViewModel 的文件夹,然后右键单击该文件夹并创建一个名为“BlogCommentViewModel.cs”的类文件。

  public class BlogCommentViewModel
    {
        public BlogModel Blog { get; set; }

        public List<CommentModel> Comments { get; set; }
    }

在上面的 ViewModelBlogCommentViewModel)中,我们创建了一个名为 Blog 的属性,其类型为 BlogModel,因为我们想在我们的视图/页面上显示单个博文的详细信息;以及一个名为 Comments 的属性,其类型为 **List<CommentModel>**,因为我们想显示与该博文关联的评论列表。

注意:我们可以将 ViewModelBlogCommentViewModel)放在项目中的任何文件夹下,但我创建了一个名为 ViewModel 的文件夹,我将把所有为本项目创建的 ViewModel 都放在那里。

现在是时候创建一个名为 BlogController 的控制器,并在其中创建一个名为 GetBlogComment 的操作方法,如下所示。

  public class BlogController : Controller
    {
        public ActionResult GetBlogComment()
        {
            return View();
        }
    }

现在,首先创建一个视图 Model 的对象,然后设置 BlogComments 属性,如下所示。我为 BlogGetBlogModel())和 CommentGetCommentModel())创建了两个假数据函数。

public class BlogController : Controller
    {
        public ActionResult GetBlogComment()
        {
            BlogCommentViewModel BCVM = new BlogCommentViewModel();
            BCVM.Blog = GetBlogModel();
            BCVM.Comments = GetCommentModel();
            return View(BCVM);
        }

        public BlogModel GetBlogModel()
        {
           BlogModel bModel = new BlogModel() { BlogID = 1,
                                                BlogTitle = "MVC Blog", 
                                                BlogDescription = "This is MVC Blog", 
                                                CreatedDate = DateTime.Now };
            return bModel;
        }

        public List GetCommentModel()
        {
            List cModel = new List();

            cModel.Add(new CommentModel() { BlogID = 1, CommentID = 1, 
                                            Comment = "Good One", CommentedBy = "Vijay" });
            cModel.Add(new CommentModel() { BlogID = 1, CommentID = 2, 
                                            Comment = "Nice", CommentedBy = "Nishant" });
            cModel.Add(new CommentModel() { BlogID = 1, CommentID = 2, Comment = "Perfect", 
                                            CommentedBy = "Saurabh" });
            return cModel;
        }
    }

注意:不要忘记添加 ModelsViewModels 的命名空间。

现在右键单击 GetBlogComment() 操作方法,选择 **Add View**,在创建视图时,选择“**Create a Strongly-Typed view**”,并选择名为 'BlogCommentViewModel' 的视图模型,然后在 scaffold Template 中选择“**Empty**”,如下所示。

现在更改您创建的视图(GetBlogComment.cshtml)如下。

我还向 Style.css 添加了一些表格 CSS。

table, td, th {
        border: 1px solid #ddd;
        text-align: left;
    }
    table {
        border-collapse: collapse;
        width: 100%;
    }
    th, td {
        padding: 15px;
    }

现在运行应用程序,并在浏览器中输入 /Blog/GetBlogComment(ControllerName/ActionName),我们将看到如下输出。

2. ViewBag

我们使用 ViewBag 来将数据从控制器传递到视图。ViewBag 使用了 C# 4.0 中新增的动态特性的属性。在使用 ViewBag 时,不需要类型转换。因此,我们可以借助 ViewBag 将多个数据从控制器传递到视图。ViewBagControllerBase 类的一个属性。ViewBag 的作用域仅限于当前请求。

为了传递多个模型,我们将在操作方法中创建两个 ViewBag,如下所示。

    public ActionResult GetBlogComment()
        {
            ViewBag.Blog = GetBlogModel();
            ViewBag.Comments = GetCommentModel();
            return View();
        }

现在根据 ViewBag 更改您的视图(GetBlogComment.cshtml)如下。

运行应用程序,我们将获得所需的输出。

3. ViewData

ViewData 用于将数据从控制器传递到视图。ViewData 的作用域仅限于当前请求。ViewData 在获取数据时需要类型转换。ViewDataControllerBase 类的一个属性。ViewDataViewDataDictionary 类的派生类,因此我们可以使用“键/值”对其进行访问。

要使用 ViewData 来处理多个模型,请如下更改您的操作方法。

   public ActionResult GetBlogComment()
        {
            ViewData["Blog"] = GetBlogModel();
            ViewData["Comments"] = GetCommentModel();

            return View();
        }

现在根据 ViewData 更改您的视图(GetBlogComment.cshtml)如下。

运行应用程序,我们将获得与方法 1 相同的结果。

4. TempData

TempData 也是一个字典,派生自 TempDataDictionary 类。TempData 存储在短生命周期的会话中。我们也可以通过 TempData 传递多个模型。

如下更改您的操作方法。

   public ActionResult GetBlogComment()
        {
            TempData["Blog"] = GetBlogModel();
            TempData["Comments"] = GetCommentModel();

            return View();
        }

现在根据 TempData 更改您的视图(GetBlogComment.cshtml)如下。

现在运行应用程序,我们将获得所需的结果。

5. Session

Session 用于在 MVC 应用程序中跨控制器传递数据。Session 数据永不过期(默认会话过期时间为 20 分钟,但可以增加)。Session 对所有请求都有效,而不是仅限于单个重定向。

对于 Session,请如下更改您的操作方法。

   public ActionResult GetBlogComment()
        {
            Session["Blog"] = GetBlogModel();
            Session["Comments"] = GetCommentModel();

            return View();
        }

现在根据 Session 更改您的视图(GetBlogComment.cshtml)如下。

运行应用程序,我们将获得预期的结果。

6. Dynamic (ExpandoObject)

ExpandoObject 类使我们能够在运行时添加和删除成员。所以,如果我们想在运行时动态地构建一个对象进行处理,那么我们可以使用 ExpandoObject

我将不会深入探讨 ExpandoObject 的细节。让我们看看如何将 ExpandoObject 与多个模型一起使用。

如下更改操作方法。

   public ActionResult GetBlogComment()
        {         
            dynamic BCVM = new ExpandoObject();
            BCVM.Blog = GetBlogModel();
            BCVM.Comments = GetCommentModel();
            return View(BCVM);
        }

注意:要使用 ExpandoObject,请添加 System.Dynamic 命名空间。

如下更改您的视图。

运行应用程序,我们将获得预期的结果。

7. Tuples (元组)

Tuple 是一个有序序列,包含不可变的、固定大小的异构对象。Tuple 中的每个对象都具有特定的类型。

Tuple 在编程中并不是新概念。它们已经在 F#、Python 和数据库中使用。它们对 C# 来说是新的。Tuple 是在 C# 4.0 中随着动态编程一起引入的。

我们也可以使用 Tuple 来将多个模型传递给视图,如下所示。

   public ActionResult GetBlogComment()
        {
            var BCVM = new Tuple<BlogModel, 
                       List<CommentModel>>(GetBlogModel(), GetCommentModel());
            return View(BCVM);
        }

现在更改您的视图如下。

运行应用程序以查看结果。

8. Render Action

Render Action 是特殊的控制器方法,仅用于从视图调用。我们创建 Render Action 方法的方式与创建常规 Action 方法相同。

一个 Render Action 是控制器类中的一个 public 方法。Render Action 是从视图而不是从 URL 调用的,所以我们应该使用 [ChildActionOnly] 属性来装饰 RenderAction

我将创建两个 Action 方法,从这些方法中返回部分视图的结果,并通过 RenderAction 方法将这些结果渲染到视图上。

  public ActionResult GetBlogComment()
        {
            return View();
        }

        public PartialViewResult RenderBlog()
        {
            return PartialView(GetBlogModel());
        }

        public PartialViewResult RenderComments()
        {
            return PartialView(GetCommentModel());
        }

右键单击 RenderBlog() 并像下面一样添加一个视图(确保我们需要创建一个部分视图,因此请勾选该选项)。

现在像下面一样更改您创建的部分视图(RenderBlog.cshtml)的代码。

同样,右键单击 RenderComments 并像下面一样创建另一个部分视图。

像下面一样更改这个部分视图(RenderComments.cshtml)。

现在是将这两个部分视图渲染到我们主视图 GetBlogComment.cshtml 的时候了。

现在运行应用程序,我们将获得所需的结果。

9. JSON

我们也可以通过 Json 来绑定多个模型。我们将从 Action 方法返回 JsonResult,然后在视图中通过 JQuery 解析 JSON 数据并在视图上进行绑定。代码如下。

更改您的操作方法,以便 Action 方法可以返回 JSON。

   public ActionResult GetBlogComment()
        {
            return View();
        }

        public JsonResult RenderBlog()
        {
            return Json(GetBlogModel());
        }

        public JsonResult RenderComments()
        {
            return Json(GetCommentModel());
        }

如下更改您的视图。

10. 导航属性

如果两个模型相关,那么我们可以将一个模型作为一个属性绑定到另一个模型中,并将其传递给视图。例如,我们有一个 Blog 模型和一个 Comment 模型,一篇博文可以包含多个评论,所以我们可以在 Blog 模型中声明一个导航属性,如下所示。

  public class BlogModel
    {
        public int BlogID { get; set; }
        public string BlogTitle { get; set; }
        public string BlogDescription { get; set; }
        public DateTime CreatedDate { get; set; }

        public List<CommentModel> Comments { get; set; } //Navigation Property

    }

现在像下面一样更改您的 GetBlogModel() 函数和 GetBlogComment() Action。

   public ActionResult GetBlogComment()
        {
            BlogModel BM = GetBlogModel();
            return View(BM);
        }

        public BlogModel GetBlogModel()
        {
            BlogModel bModel = new BlogModel()
            {
                BlogID = 1,
                BlogTitle = "MVC Blog",
                BlogDescription = "This is MVC Blog",
                CreatedDate = DateTime.Now,
                Comments = GetCommentModel() //Add Comments here
            };
            return bModel;
        }

        public List<CommentModel> GetCommentModel()
        {
            List<CommentModel> cModel = new List<CommentModel>();

            cModel.Add(new CommentModel() 
            { BlogID = 1, CommentID = 1, Comment = "Good One", CommentedBy = "Vijay" });
            cModel.Add(new CommentModel() 
            { BlogID = 1, CommentID = 2, Comment = "Nice", CommentedBy = "Nishant" });
            cModel.Add(new CommentModel() 
            { BlogID = 1, CommentID = 2, Comment = "Perfect", CommentedBy = "Saurabh" });
            return cModel;
        }

现在像下面一样创建您的视图。

最后,如果这篇文章有帮助,您喜欢这篇文章,请投票。

历史

  • 2016 年 6 月 26 日:初稿
© . All rights reserved.