7 天学习 MVC 项目 - 第 2 天






4.79/5 (237投票s)
在本文中,我们将在7天内逐步学习MVC 5 - 第二天。
引言
我们相信您在来到这里之前已经成功完成了第一天的学习。
第一天主要关注
- 为什么选择MVC?
- Asp.Net Web Forms 与 Asp.Net MVC 对比
- 理解控制器(Controllers)和视图(Views)
注意
如果您没有完成前一天的学习,请务必先完成。我们的目标是在这一天结束时,使用最佳实践和现代方法创建一个小型MVC项目。在接下来的每一个实验中,我们都将为之前的实验添加新功能或使其更完善。
完整系列
第二天议程
理解 Asp.Net MVC 中的视图模型(View Model)
将数据从控制器传递到视图
实验 2 中创建的视图非常静态。在实际场景中,它将显示一些动态数据。在下一个实验中,我们将向视图显示一些动态数据。
视图将以模型(Model)的形式从控制器获取数据。
模型
在 Asp.Net MVC 中,模型代表业务数据。
实验 3 – 使用 ViewData
ViewData 是一个字典,其中包含要在控制器和视图之间传递的数据。控制器将项添加到此字典,视图从中读取。让我们做一个演示。
步骤 1 - 创建模型类
在 Models 文件夹下创建一个名为 Employee 的新类,如下所示。
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Salary { get; set; }
}
步骤 2 - 在控制器中获取模型
在 GetView 方法中创建一个 Employee 对象,如下所示。
Employee emp = new Employee();
emp.FirstName = "Sukesh";
emp.LastName="Marla";
emp.Salary = 20000;
注意:请务必在顶部添加 using 语句,否则我们将不得不使用 Employee 类的完全限定名。
using WebApplication1.Models;
步骤 3 – 创建 ViewData 并返回视图
如下将 Employee 对象存储在 viewdata 中。
ViewData["Employee"] = emp;
return View("MyView");
步骤 4 - 显示视图中的 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>
步骤 5- 测试输出
按 F5 并测试应用程序。
实验 3 讲解
在 Razor 代码中使用大括号(即“{”和“}”)和不使用大括号编写 Razor 代码有什么区别?
在最后一个实验中,@emp.FirstName 可以被以下代码片段替换:
@{
Response.Write(emp.FirstName);
}
不带大括号的 @ 会简单地显示变量或表达式的值。
为什么需要类型转换?
ViewData 在内部保存的是对象。每次向其中添加新值时,都会将其装箱为 object 类型。
因此,每次尝试从中提取值时都需要拆箱。
“@emp.FirstName @emp.LastName”是什么意思?
这意味着显示名字,后跟一个空格,然后是姓氏。
能否用单个 @ 关键字写同样的东西?
可以,语法如下:@(emp.FirstName+” “+emp.LastName)
为什么在控制器中创建硬编码的 Employee 类?
仅用于演示目的。在实际情况中,我们可能会从数据库、WCF、Web 服务或其他地方获取。
数据库逻辑/数据访问层和业务层呢?
- 数据访问层是 Asp.Net MVC 中未被明确提及的层之一。它始终存在,但从未包含在 MVC 定义中。
- 业务层如前所述,它是 Model 的一部分。
完整的 MVC 结构
实验 4 – 使用 ViewBag
ViewBag 只是 ViewData 的语法糖。ViewBag 使用 C# 4.0 的动态功能,使 ViewData 动态化。
ViewBag 内部使用 ViewData。
步骤 1 – 创建 View Bag
继续使用实验 3,将步骤 3 替换为以下代码片段。
ViewBag.Employee = emp;
return View("MyView");
步骤 2 - 显示 EmployeeData 在视图中
将步骤 4 更改为以下代码片段。
@{
WebApplication1.Models.Employee emp = (WebApplication1.Models.Employee)
ViewBag.Employee;
}
Employee Details
Employee Name: @emp.FirstName @emp.LastName
Employee Salary: @emp.Salary.ToString("C")
步骤 3 - 测试输出
按 F5 并测试应用程序。
实验 4 讲解
我们可以将 ViewData 传递并作为 ViewBag 获取吗?
可以,反之亦可。正如我之前所说,ViewBag 只是 ViewData 的语法糖。
ViewData 和 ViewBag 的问题
ViewData 和 ViewBag 是在控制器和视图之间传递值的不错选择。但在实际项目中,使用它们都不是一个好的做法。让我们讨论使用 ViewData 和 ViewBag 的几个缺点。
性能问题
ViewData 中的值是 Object 类型。在使用之前,我们必须将值转换为正确的类型。这会增加额外的性能开销。
无类型安全性,无编译时错误。
如果我们尝试将值转换为错误的类型,或者在检索值时使用错误的键,我们将遇到运行时错误。作为良好的编程实践,错误应在编译时处理。
发送的数据和接收的数据之间没有 proper 连接。
作为开发人员,我个人认为这是一个主要问题。
在 MVC 中,控制器和视图之间是松散耦合的。控制器完全不知道视图中发生了什么,视图也不知道控制器中发生了什么。
从控制器,我们可以传递一个或多个 ViewData/ViewBag 值。现在,当开发人员编写视图时,他/她必须记住从控制器传来什么。如果控制器开发人员与视图开发人员不同,则会更加困难。完全不了解。这会导致许多运行时问题和开发效率低下。
实验 5 - 理解强类型视图
ViewData 和 ViewBag 的所有这三个问题的根本原因是数据类型。ViewData 中值的数类型是“Object”。
如果我们能以某种方式设置需要在控制器和视图之间传递的数据类型,问题就会得到解决,这就是强类型视图(strongly typed Views)发挥作用的地方。
让我们做一个演示。这次我们将把视图的需求提升到一个新的水平。如果工资大于 15000,它将显示为黄色,否则显示为绿色。
步骤 1 – 使视图成为强类型视图
在视图的顶部添加以下语句:
@model WebApplication1.Models.Employee
上面的语句使我们的视图成为 Employee 类型的强类型视图。
步骤 2 – 显示数据
现在,在视图中,只需键入 @Model,然后输入点(.),在 intellisense 中您将获得 Model (Employee) 类中的所有属性。
编写以下代码显示数据:
Employee Details
Employee Name : @Model.FirstName @Model.LastName
@if(Model.Salary>15000)
{
Employee Salary: @Model.Salary.ToString("C")
}
else
{
Employee Salary: @Model.Salary.ToString("C")
}
步骤 3 – 从控制器操作方法传递模型数据
将操作方法中的代码更改为如下:
Employee emp = new Employee();
emp.FirstName = "Sukesh";
emp.LastName="Marla";
emp.Salary = 20000;
return View("MyView",emp);
步骤 4 – 测试输出
实验 5 讲解
每次都需要在视图中键入完全限定的类名(Namespace.ClassName)吗?
不,我们可以添加 using 语句。
@using WebApplication1.Models
@model Employee
必须始终将视图设为强类型视图,还是有时可以继续使用 ViewData 或 ViewBag?
作为最佳实践,始终将视图设为强类型视图。
能否将我们的视图设为多个模型的强类型视图?
不能。在实际项目中,我们经常会遇到需要在同一视图中显示多个模型的情况。这个需求的解决方案将在下一个实验中讨论。
理解 Asp.Net MVC 中的视图模型(View Model)
在实验 5 中,我们违反了 MVC 原则。根据 MVC,V(即视图)应该是纯 UI。它不应包含任何类型的逻辑。我们做了以下三件事,这完全违反了 MVC 架构规则。
- 拼接名字和姓氏并显示为全名 - 逻辑
- 显示带货币的工资 - 逻辑
- 根据值显示不同颜色的工资。简单来说,根据某个值更改 HTML 元素的显示 - 逻辑
除了这三个问题之外,还有一点值得讨论。
假设我们遇到一种情况,我们想在视图中显示多种类型的数据。
例如 – 显示当前登录用户的名字以及员工数据。
我们可以通过以下任一方式来实现:
- 向 Employee 类添加 UserName 属性 – 每次我们想在视图中显示新数据时,向 Employee 类添加新属性似乎不合逻辑。这个新属性可能与 Employee 相关,也可能不相关。这也违反了 SOLID 原则的 SRP。
- 使用 ViewBag 或 ViewData – 我们已经讨论了使用此方法的弊端。
视图模型(ViewModel)的解决方案
ViewModel 是 Asp.Net MVC 应用程序中未被明确提及的层之一。它位于 Model 和 View 之间,并充当视图的数据容器。
Model 和 ViewModel 之间的区别?
Model 是业务特定数据。它将根据业务和数据库结构创建。ViewModel 是视图特定数据。它将根据视图创建。
它是如何工作的?
很简单。
- 控制器处理用户交互逻辑,或者简单来说,处理用户的请求。
- 控制器获取一个或多个模型数据。
- 控制器将决定哪个视图最适合作为对正确请求的响应。
- 控制器将根据检索到的模型数据,为视图创建并初始化 ViewModel 对象。
- 控制器将通过 ViewData/ViewBag/Strongly typed View 将 ViewModel 数据传递给视图。
- 控制器将返回视图。
视图和 ViewModel 在这里将如何连接?
视图将成为 ViewModel 类型的强类型视图。
Model 和 ViewModel 将如何连接?
Model 和 ViewModel 应该相互独立。控制器将根据一个或多个 Model 对象创建和初始化 ViewModel 对象。
让我们做一个小实验来更好地理解它。
实验 6 – 实现视图模型(View Model)
步骤 1 – 创建文件夹
在项目中创建一个名为 ViewModels 的新文件夹。
步骤 2 – 创建 EmployeeViewModel
为此,让我们列出视图的所有需求:
- First Name 和 Last Name 在显示前应被拼接。
- Amount 应以货币形式显示。
- Salary 应以不同的颜色显示(基于值)。
- Current 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;}
}
请注意,在 View Model 类中,FirstName 和 LastName 属性被替换为一个名为 EmployeeName 的属性,Salary 属性的数据类型为 string,并且添加了两个新属性:SalaryColor 和 UserName。
步骤 3 – 在视图中使用 View Model
在实验 5 中,我们将视图设为 Employee 类型的强类型视图。将其更改为 EmployeeViewModel。
@using WebApplication1.ViewModels
@model EmployeeViewModel
步骤 4 – 在视图中显示数据
用以下代码片段替换视图部分中的内容。
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>
在 GetView 操作方法中,获取模型数据并将其转换为 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);
}
步骤 5 – 测试输出
按 F5 并测试输出。
与实验 5 相同的输出,但这次视图不包含任何逻辑。
实验 6 讲解
这是否意味着每个模型都有一个 View Model?
不,每个视图都有其对应的 ViewModel。
在 Model 和 ViewModel 之间建立某种关系是否是好的做法?
不,作为最佳实践,Model 和 ViewModel 应该相互独立。
是否应始终创建 ViewModel?如果视图不包含任何表示逻辑,并且只想像 Model 一样显示 Model 数据怎么办?
我们应该始终创建 ViewModel。每个视图都应始终拥有自己的 ViewModel,即使 ViewModel 将包含与 Model 相同的属性。
假设我们遇到一种情况,视图不包含表示逻辑,并且想像 Model 一样显示 Model 数据。假设在这种情况下我们不创建 ViewModel。
问题是,如果将来我们在 UI 中显示新数据或需要添加表示逻辑,我们可能会从头开始创建全新的 UI。
因此,最好从一开始就预留空间并创建 ViewModel。在这种情况下,初始阶段的 ViewModel 将与 Model 基本相同。
实验 7 – 带有集合的视图
在这个实验中,我们将要在视图中显示员工列表。
步骤 1 – 更改 EmployeeViewModel 类
从 EmployeeViewModel 中删除 UserName 属性。
public class EmployeeViewModel
{
public string EmployeeName { get; set; }
public string Salary { get; set; }
public string SalaryColor { get; set; }
}
步骤 2 – 创建 Collection View Model
在 ViewModel 文件夹中创建一个名为 EmployeeListViewModel 的类,如下所示。
public class EmployeeListViewModel
{
public List<EmployeeViewModel> Employees { get; set; }
public string UserName { get; set; }
}
步骤 3 – 更改强类型视图的类型
将 MyView.cshtml 设为 EmployeeListViewModel 类型的强类型视图。
@using WebApplication1.ViewModels
@model EmployeeListViewModel
步骤 4 – 在视图中显示所有员工
<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>
步骤 5 – 创建 Employee 的业务层
在这个实验中,我们将把项目提升到一个新的水平。我们将向项目中添加业务层。在 Model 文件夹中创建一个名为 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;
}
}
步骤 6 – 从控制器传递数据
public ActionResult GetView()
{
EmployeeListViewModel employeeListViewModel = new EmployeeListViewModel();
EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();
List 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);
}
步骤 7 – 执行并测试输出
按 F5 并执行应用程序。
实验 7 讲解
能否将视图设为 List 的强类型视图?
是的,可以。
为什么我们创建一个名为 EmployeeListViewModel 的单独类,而不是将视图设为 List<EmployeeListViewModel> 类型的强类型视图?
如果我们使用 List
- 管理未来的表示逻辑。
- 其次是 UserName 属性。UserName 与单个员工无关。它与整个视图相关。
为什么我们从 EmployeeViewModel 中删除了 UserName 属性,并将其设为 EmployeeListViewModel 的一部分?
UserName 将对所有员工都相同。将 UserName 属性保留在 EmployeeViewModel 中会增加冗余代码,并增加数据所需的总体内存。
结论
到此,我们完成了第二天的学习。在第三天,我们将把项目推向新版本。
在 Facebook、LinkedIn 或 twitter 上与我们联系,以获取新版本更新。
如需在孟买进行线下技术培训,请访问 StepByStepSchools.Net
在线培训请访问 JustCompile.com