TOC

This article is currently in the process of being translated into Chinese (~11% done).

视 图:

向视图中传递数据

一个视图 不需要模型或后端传来的任何其它数据就能够独立存在。但在大部分情况下,你需要在视图中访问由控制器生成的数据。由于你的视图不必知道你的控制器(依据“关注点分离”原则),你的控制器负责为视图生成数据。有两种主要途径来实现视图访问后端数据,一种是你通过定义强类型模型,然后将这个模型的实例传给视图;另一种是使用ViewData或ViewBag 容器,使视图能够访问到数据。现在就让我们讨论一下这两种实现方法。

请注意,本文中有一部分内容和前面的那篇快速上手文章《向你的项目添加一个视图》中的内容重叠了。

使用(视图)模型

由于我们现在学习的这项技术叫做MVC,Model(模型)-View(视图)-Controller(控制器)的英文简称,使用(视图)模型毫无疑问是最简洁,也是最常用的向视图传递数据的方法。我们在本系列文章的后续篇幅中对模型展开详细讨论,不过现在暂时只需要知道——模型本质上就是一个对象而已,通常用于表示真实世界中的一些东西。后面你也还会遇到“View Model (视图模型)”这个概念,我们在后续也会对这个概念进行深入讲解。在这块先简单提一嘴,一个视图模型就是被传入视图的那个模型。因此模型视图可以是你项目中已经存在的东西,类似于User 或是 Product对象。还可以是你专门定义的类,用来在视图中访问和呈现数据。

模型无需是一个复杂的类,理论上,模型可以是string,int或者DateTime这类东西。换句话说,.NET framework中的任何东西都可以被当作模型使用。但在实际操作中,你的模型至少得是包含几个属性的类。举个例子,用来展示产品的模型如下所示:

public class Product
{
public string Title { get; set; }

public double Price { get; set; }
}

控制器作为连接模型和视图的纽带——它可以接收用户输入,然后将其转换为视图所需的相关数据。例如,像下面这样:

public IActionResult Details(int id)
{
Product product = new Product()
{
Title = "Toilet Paper",
Price = 1.99
};
return View(product);
}

注意到 product 实例是如何传给视图的吗?我是通过调用 View() 方法,然后将该实例作为参数传入。在 Product/Details 视图中,我现在就可以将视图所需要的 Product 类进行定义了。通过在视图文件的顶部,使用 @model 指令来实现。

@model HelloMVCWorld.Models.Product

写好上面那行代码之后,你现在就可以在你的视图中使用(视图)模型及其属性了。

@model HelloMVCWorld.Models.Product

<h1>@Model.Title</h1>
Price: @Model.Price

由于你指定了一个强类型作为视图的模型,在访问该对象的成员时,你会受到 IntelliSense 功能提供的帮助。你编写的代码也会在编译过程中被检查,以确保你所使用的属性或方法可以在模型类中找到。

动态视图

如果你在没有为视图定义模型类的情况下,直接使用了 @model 指令,那么视图将不会期望任何特定类型的模型能够传递给它。不过,实际上不妨碍你这样做,你甚至可以使用对象。这种视图通常被称为动态视图,并不常用。原因是你将失去 IntelliSense 功能支持带来的好处,编译时对属性进行检查带来的好处也没了。

So, you could take the example above and remove the @model directive in the top of the View, and it would still work. In this situation, the Model property would be considered a dynamic object, where you can access properties without the compiler checking for their existence. Of course that also means that if you try to access a non-existing property, page generation will fail during runtime, resulting in a nasty exception. Therefore, dynamic views are not used a lot.

使用 ViewData/ViewBag 容器

As an alternative to the strongly typed approach to passing data to a View, you can use the so-called ViewData/ViewBag containers. You can add stuff to them from the Controller and then automatically be able to access the stored data in your Views. It's actually quite easy and you can accomplish almost the same things as if you had used a strongly typed Model. Here's an example:

public IActionResult DetailsViewData(int id)
{
ViewBag.ProductTitle = "Toilet Paper";
ViewBag.ProductPrice = 1.99;
return View();
}

由于有个可以随时进行访问的 ViewBag 属性,你现在可以很容易地在视图中访问这条数据:

<h1>@ViewBag.ProducTtitle</h1>
Price: @ViewBag.ProductPrice

The major difference here is that there's no compile-time checking of these properties, and no help from the IntelliSense when writing them. In other words, you could easily misspell a property name and you wouldn't notice until you tried using the View. Therefore you should only use the ViewData/ViewBag for very small amounts of data. However, you should remember that you are free to combine the usage of ViewData/ViewBag with a traditional, strongly typed View.

ViewData 和 ViewBag 有什么区别?

You will have noticed that I have used two terms for dealing with dynamic view data: The ViewData and ViewBag properties. These actually both refer to the same thing, which is a Dictionary of keys and values (the objects). However, the ViewBag is a DynamicObject representation of the Dictionary, allowing you to access the data as if it were properties of the ViewBag object, instead of entries in a Dictionary. You are free to use both properties interchangeably, since they always refer to the same data, so for comparison, here's an example of how it would look if you used both approaches:

在控制器中用法的不同:

public IActionResult DetailsViewData(int id)
{
ViewData["ProductTitle"] = "Toilet Paper";
ViewBag.ProductPrice = 1.99;
return View();
}

在视图中用法的不同:

<h1>@ViewData["ProducTtitle"]</h1>
Price: @ViewBag.ProductPrice

Notice how I use the ViewData and therefore a Dictionary-based approach for reading and writing the ProductTitle, while I use the object-based dot-notation to access the ProductPrice. That leaves you with a few smaller difference, e.g. the ability to check for the presence of a given key/value in the ViewData collection (using the ContainsKey() method). At the end of the day, you should use the approach that you find most appealing and then stick with it for consistency.

总结

你可以使用几种不同的方法,将控制器中的数据传给视图。但使用强类型视图是最为推荐的,你可以在其中定义视图所需类型的模型。这将会为你带来 IntelliSense 功能的支持,并在编译时对你尝试使用的属性和方法进行检查。


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!