全7 天玩转 ASP.NET MVC — 第 2 天
0. 前言
我相信在开始第 2 天的学习时,你已经顺利地完成了第 1 天的课程。
我们回顾一下第 1 天的主要关注点:
-
为什么选择 ASP.NET MVC ?
-
ASP.NET Webforms 和 ASP.NET MVC 的对比
-
理解 ASP.NET MVC 的 Controller 以及 Views
提醒:如果你还没有完成第 1 天的学习,最好先确保完成它。我们的目标是在最后一天用最佳实践和最新技术方法来创建一个小的 MVC 项目。每一天的 Lab 训练中,我们都会比之前一天增加一些实用性的功能,这样看起来会比之前的程序更趋于完美。
1. Controller 向 View 传输数据
在 Lab 2 中,View 的创建都是偏于静态的。然而在真实的场景中,View 展示的通常是一些动态数据。在下一个 Lab 中,我们将展示 View 中如何动态展示数据。
View 将从 Controller 中获取以 Model 格式展示的数据。
Model
在 ASP.NET MVC 中,Model 展示的是业务数据。
Lab 3 - 使用 ViewData
ViewData 是一个字典,它存储了 Controller 传输给 View 的数据。Controller 将向 ViewData 字典添加条目,然后 View 从这个字典里读取。现在我们开始做一个 Demo 吧。
第一步:创建一个 Model 类
在 Model 文件夹下创建一个新的类,命名为 Employee。
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Salary { get; set; }
}
第二步:从 Controller 中获取 Model
在 GetView 方法中创建一个 Employee 对象。
Employee emp = new Employee();
emp.FirstName = "Sukesh";
emp.LastName="Marla";
emp.Salary = 20000;
注意:确保在类中使用 Using 语句将 Model 引入,否则就要在编写程序时使用 Employee 类的全名。
using WebApplication1.Models;
第三步:创建 ViewData 并返回 View
在 ViewData 中存储 Employee 对象。
ViewData["Employee"] = emp;
return View("MyView");
第四步:在 View 中展示 Employee 数据
打开文件 MyView.cshtml。从 ViewData 中检索 Employee 数据并展示。
<div>
@{
WebApplication1.Models.Employee emp =
(WebApplication1.Models.Employee)ViewData["Employee"];
}
<b>Employee Details </b><br/>
Employee Name : @emp.FirstName@emp.LastName <br/>
Employee Salary: @emp.Salary.ToString("C")
</div>
第五步:测试输出
按下 F5,测试应用。
Lab 3 的 Q&A
写 Razor 代码的过程中,使用花括号「『{』和『}』」和不使用花括号,有什么区别?
在 lab 3 中,@emp.FirstName 可以使用以下代码替换。
@{
Response.Write(emp.FirstName);
}
如果在 @ 后没有使用花括号,那么它仅仅是为了展示变量或者表达式的值。
为什么需要强制转换?
ViewData 内部承载了一些对象。每一次增加一个新值,就会把它转换为 Object 类型。
所以每一次都需要强制转换来获取对象的值。
「@emp.FirstName @emp.LastName」的含义是什么?
这个意味着 LastName 展示在 FirstName 之后,并通过空格隔开。
如果只想使用一个 @ 关键字,能做到刚才的效果吗?
答案是肯定的。通过语法 @(emp.FirstName+””+emp.LastName)。
为什么在 Controller 类中要硬编码 Employee 类?
这仅仅是为了展示 demo。实际上,我们将会在数据库,WCF ,Web Service 或者其它地方获取数据。
什么是数据库逻辑,数据访问层以及业务层?
-
数据访问层在 ASP.NET MVC 中是一个未显示的层。实际上,它一直存在,但是在 MVC 的定义中从来没包含过它。
-
业务层像之前所解释的,它是 Model 的一部分。
完整的 MVC 结构。
Lab 4 - 使用 ViewBag
ViewBag 就像是 ViewData 的语法蜜糖。ViewBag 运用 C# 4.0 的动态特征,使得 ViewData 动态化。
ViewBag 内部运用 ViewData。
第一步:创建 View Bag
继续 Lab 3,然后用如下代码片段替换 Lab 3 中的第三步:
ViewBag.Employee = emp;
第二步:在 View 中展示 EmployeeData
用如下的代码片段替换 Lab3 中的第四步:
@{
WebApplication1.Models.Employee emp =
(WebApplication1.Models.Employee)ViewBag.Employee;
}
Employee Details
Employee Name: @emp.FirstName @emp.LastName
Employee Salary: @emp.Salary.ToString("C")
第三步:测试并输出
按下 F5 并测试应用程序。
Lab 4 的 Q&A
我们是否可以传输 ViewData,然后以 ViewBag 的形式获取到?
答案是肯定的。反过来也是可以的。就像我之前所提到过的,ViewBag 仅仅是 ViewData 的语法蜜糖。
ViewData 和 ViewBag 的问题
ViewData 和 ViewBag 是 Controller 与 View 之间传输数据的很好选择方式。但是在实际的项目应用中,它们之中的任何一个都不是最佳的实践方式。现在我们来讨论一下运用 ViewData 和 ViewBag 的缺点吧。
性能问题
ViewData 中的数据类型是 Object。所以我们在使用之前需要进行正确的类型转换。这个操作为性能带来了额外的负担。
没有类型安全,也没有编译时的错误。
如果我们尝试将类型转换为错误的类型,或者我们在检索对象值的时候使用错误的 Key 值,我们将会在运行时出错。但是对于一个好的编程实践而言,错误应该在编译的时候就被捕获到。
在数据传输和数据接收之间没有正确的连接
作为一个开发者,我个人认为这是一个很主要的问题。
在 MVC 中, Controller 和 View 彼此之间的连接是弱连接,松散的。
Controller 完全不会关心 View 之中发生了什么,同理,View 也完全不会关心 Controller 之中发生了什么。
从 Controller 中我们可以传输一个或者多个 ViewData 或者 ViewBag 值。现在,当一个开发者要写一个 View 时,他需要记住 Controller 将要传输什么。如果一个 Controller 开发者与 View 开发者不是同一个人,那么情况将会变得更困难。因为是完全的不关心,所以这将导致开发过程的低效率,也有可能引起运行错误。
Lab 5 - 理解强类型 Views
刚才上述关于 ViewData 和 ViewBag 的三点问题可以归结于是由数据类型所引起的。ViewData 中存储的数据类型是 「Object」。
如果以某种方式,我们能够为传输在 Controller 和 View 中的数据设置数据类型,那么问题将会迎刃而解,而这种方式便是强类型 Views。
现在让我们做一个 Demo。这次我们将会提升 View 的需求到下一个级别层次。如果薪水大于 15000,那么那么就展示为黄颜色,否则为绿颜色。
第一步:创建强类型的 View
在 View 的顶部加上如下代码:
@model WebApplication1.Models.Employee
基于这条语句,使得我们的 View 成为一个类型为 Employee 的强类型视图。
第二步:展示数据
现在,在 View 中,仅仅使用 @Model 和 Dot(.) 操作就可以智能获取 Model,即 Empolyee 的所有数据值。
写下如下代码来展示数据:
Employee Details
Employee Name : @Model.FirstName @Model.LastName
@if(Model.Salary>15000)
{
<span style="background-color:yellow">
Employee Salary: @Model.Salary.ToString("C")
</span>
}
else
{
<span style="background-color:green">
Employee Salary: @Model.Salary.ToString("C")
</span>
}
第三步:从 Controller 的 Action 方法传输 Model 数据
更改 Action 方法为如下代码片段:
Employee emp = new Employee();
emp.FirstName = "Sukesh";
emp.LastName="Marla";
emp.Salary = 20000;
return View("MyView",emp);
第四步:测试并输出
Lab 5 的 Q&A
每次在 View 中的类型声明都需要使用类的全称吗,即 Namespace.ClassName ?
答案是否定的。我们可以运用 「using」声明。
@using WebApplication1.Models
@model Employee
我们必须总是使用强类型视图吗,还是我们可以偶尔使用一下 ViewData 或者 ViewBag ?
如果想实践最佳方式,最好使用强类型视图。
我们可以为强类型视图的 View 使用多个 Model 类型吗 ?
答案是否定的。在实际项目中,当我们想要在一个视图中展示多个 Model 时,我们经常会结束在这点上。这一需求的解决方案将在下一节中讨论。
2. 理解 ASP.NET MVC 中的 View Model
在 Lab 5 中我们已经违反了 MVC 的准则。根据 MVC, V 代表的是纯粹的 UI。它应该不包含任何的逻辑。我们已经通过如下的三点违反了 MVC 的结构规则:
-
附加了 First Name 和 Last Name,并且用它们展示了全名。这属于逻辑操作。
-
以货币形式展示了 Salary。这属于逻辑操作。
-
展示了不同工资的不同颜色。这些基于不同值的简单的操作改变了 HTML 元素的外观。这属于逻辑操作。
除了以上三点,这里还有一个更值得讨论的问题点。
这一种情形是,我们想要在 View 中展示不同类型的数据。比如:显示当前登录的用户名称和雇员数据。
我们可以使用如下两种方式实现这个问题:
-
向 Employee 类增加一个 UserName 属性。每一次我们想要在视图中展示一个新数据,我们就像 Employee 类中增加一个属性。这似乎是不合理的,这个属性也许和 Employee 没有关联。这也违反了 SOLID 的 SRP 准则。
-
运用 ViewBag 或者 ViewData。这个方法我们已经在刚才讨论了其弊端。
ViewModel 解决方案
ViewModel 是 ASP.NET MVC 应用中没有声明出的层。它适合于 Model 和 View 之间并且为 View 作为一个数据容器。
Model 和 ViewModel 的区别是什么?
Model 特指业务数据。它基于业务和数据结构创建。ViewModel 特指 View 数据。它基于视图 View 创建。
ViewModel是如何工作的?
工作原理非常简单。
-
Controller 处理用户的交互逻辑,或者简单来说,处理用户请求。
-
Controller 获得一个或多个 Model 数据。
-
Controller 将决定哪个 View 为请求作出正确回应。
-
Controller 将会根据视图的需求从接收的 Model 数据中创建并初始化 ViewModel 对象。
-
Controller 将会以 ViewData/ViewBag/强类型 View 的方式传输 ViewModel 数据给 View。
-
Controller 将会返回 View。
View 和 ViewModel 将如何关联?
View 将会是一个以 ViewModel 为强类型的视图。
Model 和 ViewModel 将如何关联?
Model 和 ViewModel 彼此之间应该是独立的。Controller 将会基于一个或多个 Model 对象来创建并初始化 ViewModel 对象。
让我们做一个小的 Lab 来更好地理解它吧。
3. Lab 6 - 实现 View Model
第一步:创建一个文件夹
在项目中命名一个文件夹,命名为 ViewModels。
第二步:创建 EmployeeViewModel
为了做这一步,我们先来理清一下 View 的所有需求。
-
First Name 和 LastName 需要合并展示,所以在展示前它们应该是合并的。
-
使用货币形式来显示 Amount。
-
不同的 Salary 展示出不同的颜色。
-
当前的 User Name 也要展示在视图中。
在 ViewModels 文件夹下创建一个 EmployeeViewModel 类,如下所示:
public class EmployeeViewModel
{
public string EmployeeName { get; set; }
public string Salary { get; set; }
public string SalaryColor { get; set; }
public string UserName{get;set;}
}
需要注意的是,在这个 ViewModel 类中, FirstName 和 LastName 被一个属性所替代,即 EmployeeName。并且 Salary 的数据类型是 String,除此之外,又增加了两个属性,即 SalaryColor 和 UserName。
第三步:在 View 中运用 ViewModel
在 Lab 5 中,我们将 View 强类型为 Employee。现在将其强类型为 EmployeeViewModel。
@using WebApplication1.ViewModels
@model EmployeeViewModel
第四步:在 View 中展示数据。
使用如下的代码片段替换 View 中的内容:
Hello @Model.UserName
<hr />
<div>
<b> Employee Details</b><br />
Employee Name : @Model.EmployeeName <br />
<span style="background-color:@Model.SalaryColor">
Employee Salary: @Model.Salary
</span>
</div>
第五步:创建并且传输 ViewModel
在 GetView 动作方法中,获得 Model 数据,然后将其转换为 ViewModel 对象,如下所示:
public ActionResult GetView()
{
Employee emp = new Employee();
emp.FirstName = "Sukesh";
emp.LastName="Marla";
emp.Salary = 20000;
EmployeeViewModel vmEmp = new EmployeeViewModel();
vmEmp.EmployeeName = emp.FirstName + " " + emp.LastName;
vmEmp.Salary = emp.Salary.ToString("C");
if(emp.Salary>15000)
{
vmEmp.SalaryColor="yellow";
}
else
{
vmEmp.SalaryColor = "green";
}
vmEmp.UserName = "Admin"
return View("MyView", vmEmp);
}
第六步:测试并输出
按下 F5 并测试输出。
输出的结果和 Lab 5 的一样,但是这次 View 中不再包含任何逻辑。
Lab 6 的 Q&A
这是否意味着,每一个 Model 都会有一个 ViewModel?
答案是否定的。事实上,是每一个 View 都会有一个 ViewModel。
Model 和 ViewModel 之间存在一些关联是一个好的实践方式吗?
答案是否定的。作为一个最佳实践,Model 和 ViewModel 彼此之间应该是独立的,而不是关联的。
我们需要总是创建 ViewModel 吗? 如果 View 不包含任何展示逻辑并且 View 只展示 Model 的数据会怎样?
我们应该总是创建 ViewModel。每一个 View 都应该拥有它们自己的 ViewModel,即使 ViewModel 的属性和 Model 的属性完全一样。
假设一种情形,View 不包含展示逻辑,并且使用 Model 数据,而不是 ViewModel。
问题是,如果未来有了向 UI 增加新数据的需求,或者是展示逻辑的需求,那么我们就需要重新规划一个 UI 了。
所以最好是我们在一开始就创建 ViewModel 以防止需求增加。在这种情形中,ViewModel 的初始阶段几乎和 Model 是一致的。
4. Lab 7 - View 中运用 Collection
在这一节 Lab 中,我们将在 View 中展示 Employees 的列表。
第一步:改变 EmployeeViewModel 类
从 EmployeeViewModel 类中移除 UserName 属性。
public class EmployeeViewModel
{
public string EmployeeName { get; set; }
public string Salary { get; set; }
public string SalaryColor { get; set; }
}
第二步:创建集合
在 ViewModel 文件夹下创建一个类,命名为EmployeeListViewModel。
public class EmployeeListViewModel
{
public List<EmployeeViewModel> Employees { get; set; }
public string UserName { get; set; }
}
第三步:更改 View 的强类型
将 MyView.cshtml 的强类型更换为 EmployeeListViewModel。
@using WebApplication1.ViewModels
@model EmployeeListViewModel
第四步:在 View 中展示所有的雇员信息。
<body>
Hello @Model.UserName
<hr />
<div>
<table>
<tr>
<th>Employee Name</th>
<th>Salary</th>
</tr>
@foreach (EmployeeViewModel item in Model.Employees)
{
<tr>
<td>@item.EmployeeName</td>
<td style="background-color:@item.SalaryColor">@item.Salary</td>
</tr>
}
</table>
</div>
</body>
第五步:为 Employee 创建 Business Layer
在这个 Lab 中,我们将会提升我们的项目到一个新级别。我们将会向项目中增加 Business Layer。在项目中创建一个新的文件夹,命名为 BusinessLayer,然后创建一个新的类,命名为 EmployeeBusinessLayer,该类里面包含一个方法,命名为 GetEmployees。
public class EmployeeBusinessLayer
{
public List<Employee> GetEmployees()
{
List<Employee> employees = new List<Employee>();
Employee emp = new Employee();
emp.FirstName = "johnson";
emp.LastName = " fernandes";
emp.Salary = 14000;
employees.Add(emp);
emp = new Employee();
emp.FirstName = "michael";
emp.LastName = "jackson";
emp.Salary = 16000;
employees.Add(emp);
emp = new Employee();
emp.FirstName = "robert";
emp.LastName = " pattinson";
emp.Salary = 20000;
employees.Add(emp);
return employees;
}
}
第六步:从 Controller 中传数据
public ActionResult GetView()
{
EmployeeListViewModel employeeListViewModel =
new EmployeeListViewModel();
EmployeeBusinessLayer empBal =
new EmployeeBusinessLayer();
List<employee> employees = empBal.GetEmployees();
List<EmployeeViewModel> empViewModels =
new List<EmployeeViewModel>();
foreach (Employee emp in employees)
{
EmployeeViewModel empViewModel =
new EmployeeViewModel();
empViewModel.EmployeeName =
emp.FirstName + " " + emp.LastName;
empViewModel.Salary = emp.Salary.ToString("C");
if (emp.Salary > 15000)
{
empViewModel.SalaryColor = "yellow";
}
else
{
empViewModel.SalaryColor = "green";
}
empViewModels.Add(empViewModel);
}
employeeListViewModel.Employees = empViewModels;
employeeListViewModel.UserName = "Admin";
return View("MyView", employeeListViewModel);
}
第七步:执行并测试输出
按下 F5,执行应用。
Lab 7 的 Q&A
我们能将视图的强制类型为 List 吗?
答案是肯定的。我们可以。
为什么我们要创建一个单独的类,即 EmployeeListViewModel,为什么我们不使用强类型为 List< EmployeeListViewModel > 的View 呢?
如果我们直接运用 List,而不是使用 EmployeeListViewModel 类,会引起两个问题。
-
未来也许会出现展示逻辑的需求。
-
UserName 属性。因为 UserName 和 Employees 是没有关联的。它是与一个完整的 View 相关联。
为什么我们要从 EmployeeViewModel 中移除 UserName 属性,然后把它作为EmployeeListViewModel 中的一部分呢?
UserName 对所有雇员都是一样的,如果将 UserName 的属性保留在 EmployeeViewModel 中就会增加了冗余代码,也会为增加数据的传输额外内存空间。
5. 结语
我们已经完成了第 2 天的 MVC 学习。在第 3 天中我们将使得项目进入下一个阶段。
让我一起在学习中尽情徜徉吧!
原文地址:Learn MVC Project in 7 days
本文系 OneAPM 工程师编译整理。OneAPM 是中国基础软件领域的新兴领军企业,能帮助企业用户和开发者轻松实现:缓慢的程序代码和 SQL 语句的实时抓取。想阅读更多技术文章,请访问 OneAPM 官方博客。