Pro ASP.NET Core MVC2(第7版)翻译

第5章:使用 Razor

作者:Adam Freeman 翻译:陈广 日期:2018-8-24


在 ASP.NET Core MVC 应用程序中有一个叫视图引擎的组件,它用于生成发送到客户端的内容。默认的视图引擎称为 Razor,它处理带标注的HTML文件,以便将动态内容插入到发送给浏览器的输出中。

在本章中,我将简要介绍 Razor 语法,以便您在看到 Razor 表达式时能够识别它们。我不打算在本章中提供详尽的 Razor 参考资料;就把它当成是语法速成课吧。本书的后续章节我会在介绍其它 MVC 特性内容时深入探讨 Razor。表5-1简单介绍了 Razor。

表 5-1:Razor 问答

问题 回答
它是什么? Razor 是视图引擎,负责将数据合并到 HTML 文档中。
它有什么用? 动态生成内容的能力对于编写 Web 应用程序至关重要。Razor 提供的功能使其易于通过 C# 语句编写 ASP.NET Core MVC 剩下的部分。
如何使用它? Razor 表达式可添加到视图文件的静态 HTML 中。对表达式进行计算以生成对客户端请求的响应
是否有任何缺陷或限制? Razor 表达式几乎可以包含任何 C# 语句,你很难决定逻辑应当放在视图还是控制器,这可能会消弱对 MVC 模式至关重要的关注点分离。
有没有其他选择? 您可以编写自己的视图引擎,正如我在第21章中解释的那样。有一些可用的第三方视图引擎,但往往针对小众领域,并不具备长期吸引力。

表 5-2:章节摘要

问题 解决方案 清单
访问视图模型 使用@model表达式定义模型类型,并使用@Model表达式访问模型对象 5,14,17
在不使用命名空间限定的情况下使用类型名称。 创建 view imports 文件 6,7
定义将由多个视图使用的内容 使用布局 8-10
指定默认布局 使用 view start 文件 11-13
将数据从控制器传递到视图模型之外的视图。 使用 view bag 15-16
选择性生成内容 使用 Razor 条件表达式 18,19
为数组或集合的每个项生成内容 使用 Razor foreach表达式 20-21

准备示例项目

为演示 Razor 是如何工作的,我使用【空】模板创建了一个名为【Razor】的【ASP.NET Core Web应用程序(.NET Core)】项目,就像上一章一样。通过对Startup类进行清单5-1所示的更改,我启用了 MVC 框架。

清单 5-1:Razor 文件夹下的 Startup.cs 文件,启用 MVC。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace Razor
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            //app.Run(async (context) =>
            //{
            //    await context.Response.WriteAsync("Hello World!");
            //});
            app.UseMvcWithDefaultRoute();
        }
    }
}

定义模型

接下来,新建一个 Models 文件夹,并向其添加一个名为 Product.cs 的类文件,用于定义如清单5-2所示的简单的模型类。

清单 5-2:Models 文件夹下的 Product.cs 文件的内容

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Razor.Models
{
    public class Product
    {
        public int ProductID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public string Category { set; get; }
    }
}

创建控制器

我在 Startup.cs 文件中设置的默认配置遵循 MVC 惯例,默认情况下向名为 Home 的控制器发送请求。我创建了一个 Controllers 文件夹,并在其中添加了一个名为 HomeController.cs 的类文件,用于定义清单5-3所示的简单控制器。

清单 5-3:Controllers 文件夹下的 HomeController.cs 文件的内容

using Microsoft.AspNetCore.Mvc;
using Razor.Models;

namespace Razor.Controllers
{
    public class HomeController : Controller
    {
        public ViewResult Index()
        {
            Product myProduct = new Product
            {
                ProductID = 1,
                Name = "Kayak",
                Description = "A boat for one person",
                Category = "Watersports",
                Price = 275M
            };
            return View(myProduct);
        }
    }
}

控制器定义了一个叫Index的 action 方法,在其中创建和填充Product对象的属性。我将Product传递给View方法,以便在渲染视图时将它用作模型。在调用View方法时,我并没有指定视图名称,所以将会使用 acton 方法的默认视图。

创建视图

为创建Index action 方法的默认视图,我创建了一个 Views/Home 文件夹,并向其中添加了一个名为 Index.cshtml 的 MVC 视图页面文件,并添加如清单5-4所示的内容。

清单 5-4:Views/Home 文件夹下的 Index.cshtml 文件的内容

@model Razor.Models.Product

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    Content will go here
</body>
</html>

在接下来的部分中,我将介绍 Razor 视图的不同部分,并演示您可以使用 Razor 视图做的一些不同的事情。在学习 Razor 时,请牢记视图用于向用户表示模型的一个或多个部分,这意味着生成的 HTML 所显示的数据是来自一个或多个对象的。如果您还记得我一直在尝试构建一个可以发送到客户端的 HTML 页面,那么 Razor 所做的一切都是有意义的。如果运行应用程序,您将看到图5-1所示的简单输出。

图5-1 运行示例应用程序

使用 Model 对象

让我们从 Index.cshtml 视图文件的第一行代码开始:

@model Razor.Models.Product

Razor 表达式始于@字符。本例中的@model表达式声明了我要从 action 方法传递到视图的模型对象。这允许我通过@Model引用视图模型对象的方法、字段和属性,如清单5-5所示,Index 视图中附加的简单内容。

清单 5-5:Views/Home 文件夹下的 Index.cshtml 文件,引用视图模型对象属性

@model Razor.Models.Product

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    @Model.Name
</body>
</html>

注意:注意,我在声明模型对象类型时使用了@model(小写 m),而在访问Name属性时使用了@Model(大写 M)。当您开始使用 Razor 时,这有点令人困惑,但它很快就变成了第二天性。

如果运行应用程序,将会看到如图5-2所示的输出。 图5-2 在视图中读取属性的效果

使用@model表达式指定类型的视图被称为强类型视图。当您键入@Model并在后面加小数点时,Visual Studio 会使用@model表达式弹出成员名称建议,如图5-3所示。 图5-3 Visual Studio 基于 @Model 表达式提供的成员名称建议

Visual Studio 为成员名称所提供的建议帮助避免了 Razor 视图中的错误。您可以忽略这些建议,Visual Studio 将突出显示成员名称的问题,以便您进行更正,就和常规 C# 类文件一样。您可以在图5-4中看到一个问题高亮的示例,我试图引用 @Model.NotARealProperty。Visual Studio 已经意识到我在模型类型中指定的Product类没有这样的属性,并且在编辑器中高亮显示了这个错误。

图5-4 Visual Studio 报告 @Model 表达式的一个错误

使用 View Imports(视图导入)

当我在 Index.cshtml 文件的开头定义一个模型对象时,将不得不包含模型类所在的命名空间,如下:

...
@model Razor.Models.Product
...

默认情况下,强类型 Razor 视图中引用的所有类型都必须使用命名空间限定。当仅针对模型对象进行类型引用时,这不是什么大不了的事,但是,当编写更复杂的 Razor 表达式时(如我在本章后面描述的那些),它可能会使视图更难阅读。

您可以在项目中添加一个 view imports 文件,并在其中指定一系列将要被搜索的类型的命名空间。view imports 文件存放于 Views 文件夹,它的名称为 _ViewImports.cshtml。

注意:Views 文件夹下命名以下划线_开始的文件不会返回给用户,这样就可以通过文件名来区分哪些视图用于渲染,哪些视图只是用于辅助其它视图的。View Imports 文件和 layouts 文件(等下会讲)以下划线为前缀。

译者注:这里所说的不返回给用户是指服务器不会渲染此视图,并返回给客户端在浏览器中显示,它仅是具有辅助功能的视图。

要创建一个 view imports 文件,请在【解决方案资源管理器】中右键单击【Views】文件夹,在弹出菜单中选择【添加】 ➤ 【新建项】,在【ASP.NET Core】 ➤ 【Web】类别下选择【Razor 视图导入】模板,如图5-5所示。

图5-5 创建 view imports 文件

Visual Studio 自动设置文件的名称为 _ViewImports.cshtml,单击【添加】按钮将会创建一个空文件,添加如清单5-6所示的表达式。

清单 5-6:Views 文件夹下的 _ViewImports.cshtml 文件的内容

@using Razor.Models

在 Razor 视图中使用的类的命名空间需要通过@using表达式后跟命名空间来指定。在清单5-6中,为包含示例应用程序模型类,使用了Razor.Models命名空间限定名。

现在Razor.Models命名空间已经包含在 view imports 文件中,我可以从 Index.cshtml 文件中将此命名空间删除,如清单5-7所示。

清单 5-7:Views/Home 文件夹下的 Index.cshtml 文件,省略命名空间

@model Product

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    @Model.Name
</body>
</html>

提示:你也可以在单独的视图文件中添加@using表达式,这样就可以在单个视图中不通过命名空间限定来使用类型。

使用布局(Layout)

在 Index.cshtml 视图文件中还有另一个重要的 Razor 表达式:

...
@{
    Layout = null;
}
...

这是一个 Razor 代码块示例,它使得我可以在一个视图中包含 C# 代码。代码块从@{开始,结束于},它包含的语句在渲染视图时进行计算。

上述代码块将Layout属性值设置为null。在 MVC 应用程序中,Razor 视图被编译成 C# 类,所使用的基类定义了Layout属性。我将在21章展示所有这些是如何工作的。将Layout属性设置为null是告诉 MVC:视图是自包含的(self-contained),并将渲染客户端所需的所有内容。

译者注:作者所说的 self-contained 表示在这个视图文件里已经包含了所有需要渲染的东西,没有加入其它文件的内容,也就是引入公共布局,分部视图之类的东西。

对于简单的示例应用程序来说,自包含的视图是不错的选择,但是一个真正的项目可能有几十个视图,某些视图会有共享的内容。在视图中重复编写本应共享的内容会使应用程序变得很难管理,特别是当您需要进行更改并且必须跟踪所有需要更改的视图时。

更好的方法是使用 Razor 布局,它是一个包含公共内容的模板,可以应用于一个或多个视图。当您对布局进行更改时,更改将自动影响使用它的所有视图。

创建一个布局

布局通常由多个控制器使用的视图共享,并存储在名为 Veiws/Shared 的文件夹中,此文件夹是 Razor 在查找文件时要搜索的地点之一。要创建布局,请新建 Views/Shared 文件夹,在弹出菜单中选择【添加】 ➤ 【新建项】。在【ASP.NET】类别中选择【Razor 布局】模板,并将名字设置为 _BasicLayout.cshtml,如图5-6所示。单击【添加】按钮以创建文件。(和 view import 文件一样,布局文件的名称以下划线开头。)

图5-6 创建布局

清单5-8显示了 _BasicLayout.cshtml 文件的初始内容,在文件创建时由 Visual Studio 添加。

清单 5-8:Views/Shared 文件夹下的 _BasicLayout.cshtml 文件的初始内容

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
</head>
<body>
    <div>
        @RenderBody()
    </div>
</body>
</html>

布局是视图的一种特殊形式,清单中有两个@表达式。对@RenderBody方法的调用将 action 方法指定的视图的内容插入到布局标记中,如下所示:

...
<div>
    @RenderBody()
</div>
...

布局中的另一个 Razor 表达式查找一个叫ViewBag.Title的属性,用于设置title元素的内容,如下:

...
<title>@ViewBag.Title</title>
...

ViewBag是一个便利的特性,它允许数据值在应用程序中传递,本例是在视图和布局间传递。当我将布局应用到视图时,您将看到它是如何工作的。

布局中的 HTML 元素将应用于任何使用它的视图,为定义公共内容提供了一个模板。在清单5-9中,我在布局中添加了一些简单的标记,这样它的模板效果就会很明显。

清单 5-9:Views/Shared 文件夹下的 _BasicLayout.cshtml 文件,添加内容

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    <style>
        #mainDiv {
            padding: 20px;
            border: solid medium black;
            font-size: 20pt
        }
    </style>
</head>
<body>
    <h1>Product Information</h1>
    <div id="mainDiv">
        @RenderBody()
    </div>
</body>
</html>

我已经添加了一个 header 元素以及一些 CSS 来对包含@RenderBody表达式的div元素的内容进行样式化,只是为了明确哪些内容来自布局,哪些来自视图。

应用布局

要将布局应用于视图,需要设置Layout属性值并删除当前的 HTML,诸如htmlhead以及body元素,如清单5-10所示。

清单 5-10:Views/Home 文件夹下的 Index.cshtml 文件,应用布局

@model Product

@{
    Layout = "_BasicLayout";
    ViewBag.Title = "Product Name";
}

Product Name: @Model.Name

Layout属性指定了将应用于视图的布局文件的名称,无需写 cshtml 文件扩展名。Razor 将在 /Views/Home 以及 Views/Shared 文件夹下查找指定布局文件。

清单中,我还设置了ViewBag.Title属性。在渲染视图时,布局将使用它来设置title元素的内容。

即使对于这样一个简单的应用程序,视图的转换也是戏剧性的。布局包含任何 HTML 响应所需的所有结构,这使得视图只需关注向用户呈现数据的动态内容。当 MVC 处理 Index.cshtml 文件时,它通过应用布局来创建一个统一的 HTML 响应,如图5-7所示。

图5-7 给视图应用布局的效果

使用 View Start 文件

我仍然有一个小窍门要整理一下,就是我必须在每个视图中指定我想要的布局文件。如果需要重命名布局文件,我将不得不找到每个引用它的视图并一一更改,这将是一个容易出错的过程,与贯穿 MVC 开发的易于维护的一般主题背道而驰。

我枪口以使用 veiw start 文件来解决这个问题。当 MVC 在渲染视图时,会搜索一个名为 _ViewStart.cshtml 的文件。该文件的内容将被视为包含在视图文件内,我可以使用此特性自动设置Layout属性的值。

要创建一个 view start 文件,右键单击 Views 文件夹,在弹出菜单中选择【添加】➤【新建项】,并选择【ASP.NET】类别下的【Razor View Start】模板,如图5-8所示。

图5-8 创建 view start 文件

Visual Studio 会将文件名设置为 _ViewStart.cshtml,单击【添加】按钮将会创建文件,并包含如清单5-11所示的初始内容。

清单5-11:Views 文件夹下的 _ViewStart.cshtml文件的初始内容

@{
    Layout = "_Layout";
}

要将布局应用到应用程序的所有视图中,我只需更改分配给Layout属性的值即可,如清单5-12所示。

清单 5-12:Views文件夹下的 _ViewStart.cshtml 文件,应用默认视图

@{
    Layout = "_BasicLayout";
}

因为 view start 文件包含了Layout属性的值,所以我可以从 Index.cshtml 文件中删除相应的表达式,如清单5-13所示。

清单 5-13:Views/Home 文件夹下的 Index.cshtml 文件,应用 View Start 文件

@model Product

@{
    ViewBag.Title = "Product Name";
}
Product Name: @Model.Name

我不需要指定我想要使用 View Start 文件,MVC 将定位该文件并自动使用其内容。视图文件中定义的值优先,这使得重写 view start 文件变得容易。

还可以使用多个 view start 文件为应用程序的不同部分设置默认值。Razor 查找与正在处理的视图最接近的 view start 文件,这意味着您可以通过将 view start 文件添加到 Views/Home 或 Views/Shared 文件夹来覆盖默认设置。

警告:理解从视图文件中省略Layout属性与将其设置为null之间的区别是很重要的。如果您的视图是自包含的,且无需使用布局,则设置Layout属性为null。如果你省略了Layout属性,MVC 将假设您确实需要一个布局,并且会使用它在 veiw start 文件中找到的值。

使用 Razor 表达式

现在我已经向您展示了视图和布局的基础知识,接下来转向 Razor 所支持的不同类型表达式,以及如何使用它们来创建视图内容。在一个好的 MVC 应用程序中,action 方法和视图执行的角色之间存在明显的分离。规则很简单,表5-3显示了其摘要。

表 5-3:action 方法和视图所扮演的角色

组件 可以做 不做
action 方法 将视图模型对象传递给视图 将格式化数据传递给视图
视图 使用视图模型对象向用户展示内容 更改视图模型对象的任何方面

我又回到了这个贯穿本书的主题。为从 MVC 中获取最佳效果,你需要尊重和加强应用程序不同部分之间的分离。正如您将看到的,可以用 Razor 做很多事件,包括使用 C# 语句 —— 但是不应该使用 Razor 来执行业务逻辑或以任何方式操作域模型对象。清单 5-14 显示了在Index视图中添加的一个新表达式。

清单 5-14:Views/Home 文件夹下的 Index.cshtml 文件,添加一个表达式

@model Product

@{
    ViewBag.Title = "Product Name";
}
Product Name: @Model.Name

<p>Product Name: @Model.Name</p>
<p>Product Price: @($"{Model.Price:C2}")</p>

我本可以在 action 方法中格式化Price属性的值,并将其传递给视图。这是可行的,但采用这种方法会损害 MVC 模式的优势,并降低我对未来变化的响应能力。正如我所说的,我将再次回到这个主题,但是您应该记住,ASP.NET Core MVC 并没有强制应当使用 MVC 模式,您必须始终意识到您所做的设计和编码决策的效果。

数据的处理与格式化

区分处理数据和格式化数据是很重要的。视图格式化数据,这就是为什么我将上一节中的Product对象传递给视图,而不是将对象的属性格式化为显示字符串。处理数据 —— 包括选择要显示的数据对象 —— 是控制器的责任,它将调用模型来获取和修改它所需的数据。有时很难弄清楚处理和格式化之间的界限在哪里,但根据经验,我建议谨慎行事,并将除最简单的表达式之外的任何东西放到到控制器而不是视图。

插入数据值

使用 Razor 表达式可以做的最简单的事情是将数据值插入标记中。最常见的方法是使用@Model表达式。Index 视图已经包含了这种方法的示例,如下所示:

...
<p>Product Name: @Model.Name</p>
...

您还可以使用ViewBag特性插入值,这是我在布局中用来设置title元素内容的特性。ViewBag还可用于将数据从控制器传递到视图,以补充模型,如清单5-15所示。

清单 5-15:Controllers 文件夹下的 HomeController.cs 文件,使用 View Bag

using Microsoft.AspNetCore.Mvc;
using Razor.Models;

namespace Razor.Controllers
{
    public class HomeController : Controller
    {
        public ViewResult Index()
        {
            Product myProduct = new Product
            {
                ProductID = 1,
                Name = "Kayak",
                Description = "A boat for one person",
                Category = "Watersports",
                Price = 275M
            };
            ViewBag.StockLevel = 2;
            return View(myProduct);
        }
    }
}

ViewBag属性返回一个动态对象,可用于定义任意属性。清单中,我定义了一个名为StockLevel的属性,并将值 2 赋给了它。因为ViewBag属性是动态的,我无需事先声明属性名称,但这意味着 Visual Studio 无法为 view bag 属性提供自动完成的建议。

知道何时使用 view bag 和何时应该扩展模型是一个经验和个人偏好的问题。我的个人风格是仅将 view bag 用于提供关于如何渲染数据的视图提示,而不是将其用于显示给用户的数据值。但这只是我个人经验,如果你想将 view bag 用于想显示给用户的数据,那么请使用@ViewBag表达式访问值,如清单5-16所示。

清单 5-16:Views/Home 文件夹下的 Index.cshtml 文件,显示 View Bag

@model Product

@{
    ViewBag.Title = "Product Name";
}
Product Name: @Model.Name

<p>Product Name: @Model.Name</p>
<p>Product Price: @($"{Model.Price:C2}")</p>
<p>Stock Level: @ViewBag.StockLevel</p>

图5-9显示了新数据值的结果。

图5-9 使用 Razor 表达式插入数据值

设置属性值

到目前为止,所有示例都用于设置元素的内容,但也可以使用 Razor 表达式来设置元素属性值。清单5-17显示了@Model@ViewBag表达式用于设置 Index 视图中元素的属性内容。

清单 5-17:Views/Home 文件夹下的 Index.cshtml 文件,设置属性值

@model Product

@{
    ViewBag.Title = "Product Name";
}
<div data-productid="@Model.ProductID" data-stocklevel="@ViewBag.StockLevel">
    <p>Product Name: @Model.Name</p>
    <p>Product Price: @($"{Model.Price:C2}")</p>
    <p>Stock Level: @ViewBag.StockLevel</p>
</div>

我使用 Razor 表达式设置了div元素上某些数据属性的值。

提示:数据属性是名称以data-为前缀的属性,多年来一直是创建自定义属性的一种非正式方式,并作为 HTML5 的一部分成为正式标准的一部分。这些属性经常被使用,以便 JavaScript 代码能够定位指定的元素,或者使 CSS 样式可以更狭义地应用。

如果运行示例程序并查看发送到浏览器的源码,你将看到 Razor 已经设置了属性的值,如下:

<div data-productid="1" data-stocklevel="2">
<p>Product Name: Kayak</p>
<p>Product Price: £275.00</p>
<p>Stock Level: 2</p>
</div>

使用条件语句

Razor 可用于处理条件语句,这意味着我可以根据视图数据值调整视图的输出。这是 Razer 的核心技术,它允许你创建复杂而流畅但仍然易于阅读和维护的布局。清单5-18中,我更新了 Index 视图以包含条件语句。

清单 5-18:Views/Home 文件夹下的 Index.cshtml 文件,使用条件 Razor 语句

@model Product
@{
    ViewBag.Title = "Product Name";
}
<div data-productid="@Model.ProductID" data-stocklevel="@ViewBag.StockLevel">
    <p>Product Name: @Model.Name</p>
    <p>Product Price: @($"{Model.Price:C2}")</p>
    <p>
        Stock Level:
        @switch (ViewBag.StockLevel)
        {
            case 0:
                @:Out of Stock
                break;
            case 1:
            case 2:
            case 3:
                <b>Low Stock (@ViewBag.StockLevel)</b>
                break;
            default:
                @: @ViewBag.StockLevel in Stock
                break;
        }
    </p>
</div>

要开始一个条件语句,请在 C# 条件关键字前放置@字符,本例为switch。使用一个封闭大括号字符}终止代码块,就像使用常规的 C# 代码块一样。

在 Razor 代码块中,只需定义 HTML 和 Razor 表达式,就可以将 HTML 元素和数据值包含到视图输出中,如下所示:

...
<b>Low Stock (@ViewBag.StockLevel)</b>
...

我不需要将元素或表达式放在引号中或以任何特殊方式表示它们 —— Razor 引擎会将它们解析为要处理的输出。但如果您想在不包含 HTML 元素的情况下将文字插入到视图中,那么需要给 Razor 一个助手,在行的前面加上前缀,如下:

...
@: Out of Stock
...

@:字符防止 Razor 把它当成 C# 语句解析,这是它遇到文本时的默认行为。可以在图5-10中看到条件语句的结果。

图5-10 在 Razor 视图中使用 switch 语句

条件语句在 Razor 视图中很重要,因为它们允许根据视图从 action 方法接收的数据值来更改内容。作为另一个演示,清单5-19显示了在 Index.cshtml 视图中添加一个if语句,正如您可能想象的那样,这是一个常用的条件语句。

清单 5-19:Views/Home 文件夹下的 Index.cshtml 文件,在 Razor 视图中使用if语句

@model Product
@{
    ViewBag.Title = "Product Name";
}
<div data-productid="@Model.ProductID" data-stocklevel="@ViewBag.StockLevel">
    <p>Product Name: @Model.Name</p>
    <p>Product Price: @($"{Model.Price:C2}")</p>
    <p>
        Stock Level:
        @if (ViewBag.StockLevel == 0)
        {
            @:Out of Stock
}
        else if (ViewBag.StockLevel > 0 && ViewBag.StockLevel <= 3)
        {
            <b>Low Stock (@ViewBag.StockLevel)</b>
        }
        else
        {
            @: @ViewBag.StockLevel in Stock
}
    </p>
</div>

此条件语句生成与switch语句相同的结果,但我想演示如何将 C# 条件语句与 Razor 视图结合起来。我在第21章深入描述视图时解释了这是如何工作的。

数组与集合的枚举

在编写 MVC 应用程序时,您通常希望枚举数组或其他类型对象集合的内容,并生成每个对象详细内容。为了演示如何做到这一点,在清单5-20中,我修改了 Home 控制器中的Index action,将Product对象数组传递给视图。

清单 5-20:Controllers 文件夹下的 HomeController.cs 文件,使用数组

using Microsoft.AspNetCore.Mvc;
using Razor.Models;

namespace Razor.Controllers
{
    public class HomeController : Controller
    {
        public ViewResult Index()
        {
            Product[] array = {
                new Product {Name = "Kayak", Price = 275M},
                new Product {Name = "Lifejacket", Price = 48.95M},
                new Product {Name = "Soccer ball", Price = 19.50M},
                new Product {Name = "Corner flag", Price = 34.95M}
            };
            return View(array);
        }
    }
}

action 方法创建了一个包含简单数据的Product[]对象,并将它们传递给View方法,以便使用默认视图渲染数据。在清单5-21中,我更改了 Index 视图的 model 类型,并使用foreach循环枚举数组中的对象。

提示:清单5-21中的Model无需使用@字符前缀,因为它是更大的 C# 表达式的一部分。很难确定什么时候需要@字符,什么时候不需要,但是 Visual Studio 智能提示用于 Razor 文件时会通过下划线错误告诉您。

清单 5-21:Views/Home 文件夹下的 Index.cshtml 文件,枚举数组

@model Product[]

@{
    ViewBag.Title = "Product Name";
}

<table>
    <thead>
        <tr><th>Name</th><th>Price</th></tr>
    </thead>
    <tbody>
        @foreach (Product p in Model)
        {
            <tr>
                <td>@p.Name</td>
                <td>@($"{p.Price:C2}")</td>
            </tr>
        }
    </tbody>
</table>

@foreach语句枚举了 model 数组的内容,并在表格中为每项生成一行内容。您可以看到我是如何在foreach循环中创建一个名为p的局部变量,然后使用 Razor 表达式@p.Name@p.Price引用它的属性。您可以在图5-11中看到结果。

图5-11 使用 Razor 枚举数组

总结

在本章中,我向您简要介绍了 Razor 视图引擎以及如何使用它生成 HTML。我向您展示了如何通过视图模型对象和 view bag 引用从控制器传递的数据,以及如何使用 Razor 表达式根据数据值调整对用户的响应。你将在本书的其余部分看到很多关于 Razor 不同的例子,我将在21章详细描述 MVC 视图机制的工作原理。在下一章,我介绍了 Visual Studio 为 ASP.NET Core MVC 项目的使用提供的一些功能。

;

© 2018 - IOT小分队文章发布系统 v0.3