7 天逐步学习 MVC (Model View Controller) – 第 5 天






4.78/5 (62投票s)
顾名思义,这篇文章是关于学习MVC的。所以,议程很简单,我们将在7天内学习ASP.NET MVC。
MVC 2已经很老了,这篇文章是很多年前写的。我们建议您从我们最新的《7天循序渐进学习MVC 5》系列开始阅读:-https://codeproject.org.cn/Articles/866143/Learn-MVC-step-by-step-in-days-Day
- 那么,议程是什么?
- 第1天:-控制器、强类型视图和辅助类
- 第2天:-单元测试、路由和传出URL
- 第3天:-部分视图、数据注释、Razor、身份验证和授权
- 第4天:- JSON、JQuery、状态管理和异步控制器
- 第5天:-打包、最小化、视图模型、区域和异常处理
- 第6天:-显示模式、MVC OAuth、模型绑定器、布局和自定义视图引擎
- 实验 18:- MVC 捆绑和最小化
- 理解捆绑
- 理解最小化
- 步骤 1:- 使用空模板创建MVC项目
- 步骤 2:- 创建一个调用视图的控制器
- 步骤 3:- 运行并观察生成了多少个调用。
- 步骤 4:- 引用“System.Web.Optimization”
- 步骤 5:- bundle.config 文件
- 步骤 6:- 在 global.asax.cs 文件中调用 bundle.config 文件
- 步骤 7:- 在视图中渲染捆绑
- 步骤 8:- 实时查看您的捆绑和最小化
- 实验 19:- MVC 视图模型
- 理论
- 视图模型类会包含哪种类型的逻辑?
- 步骤 1:- 创建客户模型
- 步骤 2:- 创建视图模型
- 步骤 3:- 在视图中消费视图模型
- 视图模型应该是组合还是继承?
- 视图模型的优势
- 实验 20:- 异常处理
- 引言
- 步骤 1:- 添加一个会引发错误的控制器和操作
- 步骤 2:- 创建一个错误视图
- 步骤 3:- 在同一控制器内的操作之间重用(OnException)
- 步骤 4:- 在任何操作和任何控制器之间重用(FilterConfig)
- 步骤 5:- 项目级别的错误处理:- Application_Error
- 实验 21:- MVC 区域
- 什么是区域,为什么需要区域?
- 如何使用区域?
- 步骤 1:- 使用 MVC 模板添加区域。
- 步骤 2:将相关文件推送到相关区域
- 区域与文件夹有什么区别?
- 第六天讲什么?
- 您是MVC新手吗?
- 不要错过带答案的 MVC 面试题
那么,议程是什么?
在第5天,我们将研究如何通过捆绑和最小化来最大化 MVC 应用程序的性能。我们还将研究视图模型概念及其优势,视图模型充当模型和视图之间的桥梁。
第1天:-控制器、强类型视图和辅助类
https://codeproject.org.cn/Articles/207797/Learn-MVC-Model-view-controller-Step-by-Step-in-7
第2天:-单元测试、路由和传出URL
https://codeproject.org.cn/Articles/259560/Learn-MVC-Model-view-controller-Step-by-Step-in-7
第3天:-部分视图、数据注释、Razor、身份验证和授权
https://codeproject.org.cn/Articles/375182/Learn-MVC-Model-View-Controller-Step-by-Step-in-4
第4天:- JSON、JQuery、状态管理和异步控制器
https://codeproject.org.cn/Articles/667841/Learn-MVC-Model-view-controller-Step-by-Step-in-3
第5天:-打包、最小化、视图模型、区域和异常处理
https://codeproject.org.cn/Articles/724559/Learn-MVC-Model-view-controller-Step-by-Step-in-7
第6天:-显示模式、MVC OAuth、模型绑定器、布局和自定义视图引擎
https://codeproject.org.cn/Articles/789278/Learn-MVC-Model-view-controller-Step-by-Step-in-d
实验 18:- MVC 捆绑和最小化
捆绑和最小化这两个概念都有助于提高性能。
理解捆绑
Web 项目总是需要 CSS 和脚本文件。捆绑有助于在运行时将多个 JavaScript 和 CSS 文件合并为一个实体,从而将多个请求合并为一个请求,进而有助于提高性能。
例如,考虑以下对一个页面的 Web 请求。以下请求是使用 Chrome 开发者工具记录的。此页面使用了两个 JavaScript 文件“Javascript1.js”和“Javascript2.js”。因此,当请求此页面时,它会发出三个请求调用:-
- 一个用于“Index”页。
- 另外两个请求分别用于其他两个 JavaScript 文件“Javascript1.js”和“Javascript2.js”。
现在,如果您稍微考虑一下,如果有很多 JavaScript 文件(尤其是 JQUERY 文件),上述情况会变得更糟,导致大量请求,从而降低性能。
如果我们能以某种方式将所有 JS 文件合并到一个捆绑包中,并将它们作为一个单元进行请求,那将提高性能(请参阅下一张图,它只有一个请求)。
理解最小化
最小化通过删除空白字符、注释等来减小脚本和 CSS 文件的大小。例如,下面是一个带注释的简单 JavaScript 代码。
// This is test var x = 0; x = x + 1; x = x * 2;
在实现最小化后,JavaScript 代码看起来如下所示。您可以看到如何删除空格和注释以最小化文件大小,从而提高性能,因为文件大小已变得更小和压缩。
var x=0; x=x+1; x=x*2;
那么,让我们逐步演示一个使用 MVC 4.0 的捆绑和最小化的简单示例。
步骤 1:- 使用空模板创建MVC项目
为了理解捆绑和最小化,让我们创建一个空的 MVC 项目。在该项目中,让我们添加一个“Script”文件夹,并在“Script”文件夹内添加两个 JavaScript 文件,如下图所示。
以下是“Javascript1”文件的代码。
// This is test var x = 0; x = x + 1; x = x * 2;
以下是“Javascript2”文件的代码。
alert("Hello");
步骤 2:- 创建一个调用视图的控制器
现在,让我们创建一个控制器,该控制器调用一个名为“MyView”的视图,该视图使用了这两个 JavaScript 文件。
public class SomeController : Controller { // // GET: /Some/ public ActionResult MyView() { return View(); } }
以下是使用了两个 JavaScript 文件的 ASPX 视图。
<html> <script src="../../Scripts/JavaScript1.js"></script> <script src="../../Scripts/JavaScript2.js"></script> <head> </head> <body> <div> This is a view. </body> </html>
步骤 3:- 运行并观察生成了多少个调用。
现在,在 Google Chrome 中运行 MVC 应用程序,按键盘上的 CNTRL + SHIFT + I 键,您会看到以下输出。您可以看到有三个请求:-
- 第一个请求是到视图“MyView”。
- 另外两个请求分别获取“javascript1”和“javascript2”。
现在,捆绑就是将这两个 JavaScript 调用合并为一个。
步骤 4:- 引用“System.Web.Optimization”
捆绑和最小化由“System.Web.Optimization”命名空间完成。现在,这个 DLL 不是 .NET 或 ASP.NET 框架的一部分。我们需要使用 NUGET 来下载这个 DLL。所以,进入 NUGET 并搜索 ASPNET.Web.Optimization。
如果您是 Nuget 新手,不知道如何使用它,请观看此视频了解基础知识。
一旦您获得了优化程序包,请单击安装以将引用添加到您的项目中。
步骤 5:- bundle.config 文件
现在,这一步取决于您选择的 MVC 模板。如果您选择了“基本”模板,那么“BundleConfig”文件已经准备就绪;如果您选择了“空”模板,那么您需要做很多工作。
目前我们选择了“空”模板,以便我们可以从头开始了解一切。
所以,继续添加“BundleConfig”类文件,并创建一个“RegisterBundles”方法,如下面的代码所示。在下面的代码中,“bundles.add”表示将“Scripts”文件夹中的所有 JavaScript 文件添加到一个名为“Bundles”的捆绑包中。
重要提示:- 请务必在类文件中导入“using System.Web.Optimization;”,否则您会遇到错误。
public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { bundles.Add(new ScriptBundle("~/Bundles").Include( "~/Scripts/*.js")); BundleTable.EnableOptimizations = true; } }
步骤 6:- 在 global.asax.cs 文件中调用 bundle.config 文件
对于使用“基本”模板创建的项目,不需要执行此步骤。但以下是为使用“空”模板创建的人准备的步骤。打开 global.asax.cs 文件,并在应用程序启动时调用“RegisterBundles”方法,如下面的代码所示。
protected void Application_Start() { … … BundleConfig.RegisterBundles(BundleTable.Bundles); }
步骤 7:- 在视图中渲染捆绑
一旦完成捆绑,我们就需要删除“script”标签并调用“Optmization” DLL 来渲染捆绑。
<script src="../../Scripts/JavaScript1.js"></script> <script src="../../Scripts/JavaScript2.js"></script>
以下代码会将两个 JavaScript 文件捆绑为一个单元,从而避免每个文件的多个请求调用。
<%= System.Web.Optimization.Scripts.Render("~/Bundles") %>
以下是 MVC 视图中调用的完整捆绑代码。
<%= System.Web.Optimization.Scripts.Render("~/Bundles") %> <head runat="server"> <meta name="viewport" content="width=device-width" /> <title>MyView</title> </head> <body> <div> This is a view. </body> </html>
步骤 8:- 实时查看您的捆绑和最小化
现在您已经准备就绪,是时候实时查看捆绑和最小化了。所以,打开 Google Chrome,按 CNTRL + SHIFT + I,您可以看到奇迹:两个 JavaScript 文件只有一个调用。
如果您点击预览选项卡,您可以看到两个 JavaScript 文件已经合并……猜猜怎么着,是的,最小化也发生了。还记得我们的原始 JavaScript 文件吗?
// This is test var x = 0; x = x + 1; x = x * 2;
您可以在下面的输出中看到注释是如何被移除的,空格是如何被移除的,文件大小是如何变得更小、更高效的。
实验 19:- MVC 视图模型
理论
视图模型是一个简单的类,它表示要在视图中显示的数据。
例如,下面是一个简单的 CustomerModel 对象,包含“CustomerName”和“Amount”属性。
CustomerViewModel obj = new CustomerViewModel(); obj.Customer.CustomerName = "Shiv"; obj.Customer.Amount = 1000;
但是,当这个“Customer”模型对象显示在 MVC 视图上时,它看起来如下面的图所示。它在视图/屏幕上有“CustomerName”、“Amount”以及“客户购买级别”字段。“客户购买级别”是一个颜色指示,表明客户的购买活跃度。
“客户购买级别”的颜色取决于“Amount”属性的值。如果金额大于 2000,则颜色为红色;如果金额大于 1500,则颜色为橙色;否则颜色为黄色。
换句话说,“客户购买级别”是一个基于金额计算的附加属性。
因此,Customer ViewModel 类有三个属性
- “TxtCustomerName”文本框直接从“CustomerName”属性获取数据。
- “TxtAmount”文本框直接从模型中“Amount”属性获取数据。
- “CustomerBuyingLevelColor”根据“Amount”值显示颜色值。
客户模型 | 客户视图模型 |
CustomerName | TxtCustomerName |
金额 | TxtAmount |
CustomerBuyingLevelColor |
视图模型类会包含哪种类型的逻辑?
因此,视图模型类可以包含以下类型的逻辑:-
- 颜色转换逻辑:- 例如,模型中有一个“Grade”属性,您希望您的 UI 为高级等级显示“红色”,为低级等级显示“黄色”,为正常等级显示“绿色”。
- 数据格式转换逻辑:- 您的模型有一个“Status”属性,值为“Married”(已婚)和“Unmarried”(未婚)。在 UI 中,您希望将其显示为复选框,如果“已婚”则勾选,如果“未婚”则不勾选。
- 聚合逻辑:- 您有两个不同的 Customer 和 Address 模型类,并且有一个视图可以一次显示“Customer”和“Address”数据。
- 结构缩小:- 您有一个“Customer”模型,包含“customerCode”和“CustomerName”,而您只想显示“CustomerName”。因此,您可以创建一个模型包装器并公开必要的属性。
让我们做一个小的实验来理解 MVC 视图模型概念,使用我们之前讨论过的以下屏幕。
步骤 1:- 创建客户模型
我将使用自顶向下的方法来创建上述屏幕:-
- 步骤 1:- 首先创建“customer”模型。
- 步骤 2:- 创建视图模型。
- 步骤 3:- 在视图中消费视图模型。
所以,让我们继续创建一个具有以下属性的“Customer”模型。因为这是一个视图模型,所以在下面的类中我省略了颜色属性。
public class CustomerModel { private string _CustomerName; public string CustomerName { get { return _CustomerName; } set { _CustomerName = value; } } private double _Amount; public double Amount { get { return _Amount; } set { _Amount = value; } } }
步骤 2:- 创建视图模型
接下来是创建一个视图模型类,它将包装“Customer”模型并添加 UI 属性。所以,让我们创建一个“ViewModels”文件夹,并在其中添加一个“CustomerViewModel”类。
以下是“CustomerViewModel”类的代码。以下是关于视图模型类的几个重要注意事项:-
- 您可以在下面的类中看到“CustomerViewModel”类如何包装“CustomerModel”类。
- 另外,最重要的一点是我们创建了遵循 UI 命名约定的属性(TxtName、TxtAmount、CustomerLevelColor 等)。
- 现在,对于最重要的部分,请查看“CustomerLevelColor”属性的代码,它根据客户销售额显示颜色。存在一个“ViewModel”用于这种“胶水代码”/连接代码,它连接模型和视图。
public class CustomerViewModel { private CustomerModel Customer = new CustomerModel(); public string TxtName { get { return Customer.CustomerName; } set { Customer.CustomerName = value; } } public string TxtAmount { get { return Customer.Amount.ToString(); } set { Customer.Amount = Convert.ToDouble(value); } } public string CustomerLevelColor { get { if (Customer.Amount > 2000) { return "red"; } else if (Customer.Amount > 1500) { return "orange"; } else { return "yellow"; } } } }
步骤 3:- 在视图中消费视图模型
下一步是创建一个强类型 MVC 视图,我们可以在其中使用视图模型类。如果您不了解 MVC 强类型视图,请参阅 Learn MVC Day 1, Lab 4。
如果您查看视图,它现在已经修饰了,或者可以说绑定了视图模型类。最重要的一点是您的视图是干净的。它不包含用于颜色编码的决策代码。这些胶水代码已移至视图模型类中。这就是使视图模型成为 MVC 中一个非常重要的组件的原因。
此视图可以从控制器调用,该控制器传递一些示例如下代码所示的模拟数据。
public class CustomerController : Controller { // // GET: /Customer/ public ActionResult DisplayCustomer() { CustomerViewModel obj = new CustomerViewModel(); obj.Customer.CustomerName = "Shiv"; obj.Customer.Amount = 1000; return View(obj); } }
视图模型应该是组合还是继承?
许多架构师会犯错误,通过继承来创建视图模型类。如果您看到上面的视图模型类,它是通过组合而不是继承创建的。
那么,为什么组合更有意义呢?如果我们想象一下,我们从不说“这个屏幕是业务对象的子集”,这是一种奇怪的说法。
我们总是说“这个屏幕使用了那些模型”。因此,很清楚这是一种使用关系,而不是 IS A(子父)关系。
继承可能失败的一些场景是:-
- 当您不希望模型中的某些属性出现在视图中时
- 当您有一个使用多个模型的视图时。
所以,不要被通过继承模型来创建视图模型的想法所诱惑,您可能会陷入 LISKOV 问题(在此处阅读关于SOLID LISOV 的内容)。
它看起来像一只鸭子,叫声像一只鸭子,但它不是一只鸭子。它看起来像一个模型,具有像模型一样的属性,但它不是完全的模型。
视图模型的优势
- 可重用性:- 现在我的胶水代码已经进入了一个类。我可以轻松地在任何其他 UI 技术(WPF、Windows 等)中创建该类的对象。
- 测试:- 我们不再需要手动测试人员来测试 UI 的外观和感觉了。因为我们的 UI 代码已经移至类库(最小公分母),我们可以创建一个该类的对象并进行单元测试。下面是一个简单的单元测试代码,演示了 UI 外观和感觉逻辑的单元测试。您可以看到颜色测试是如何自动完成的,而不是由某个手动测试人员手动测试。
[TestMethod] publicvoid TestCustomerLevelColor() { CustomerViewModel obj = new CustomerViewModel(); obj.TxtName = "Shiv"; obj.TxtAmount = "1000"; Assert.AreEqual("Red",obj.CustomerLevelColor); }
实验 20:- 异常处理
引言
在异常处理方面,Try…Catch 块是 .NET 开发者的首选。例如,在下面的代码中,我们将操作代码包装在 TRY CATCH 块中,如果出现异常,我们将在 catch 块中调用“Error”视图。
public ActionResult TestMethod() { try { //.... return View(); } catch (Exception e) { //Handle Exception; return View("Error"); } }
上述代码最大的问题是异常处理代码的重用性。MVC 提供了在三个级别重用异常处理代码:-
- 同一控制器内的操作之间:- 您可以使用“OnException”事件,在那里您可以编写可以在同一控制器内的“操作”之间重用的逻辑。
- 任何控制器和操作之间:- 在 MVC 中,我们可以使用“FilterConfig”创建一个可以应用于任何控制器和任何操作的属性。
- 项目级别:- 在 Global.asax 中使用 Application_Error 处理程序。因此,当从 MVC 项目的任何部分引发异常时,它将路由到这个中央错误处理程序。
让我们逐步演示 MVC 中所有上述 3 种错误处理方式。
步骤 1:- 添加一个会引发错误的控制器和操作
所以,第一件事是添加一个简单的控制器和操作,它会引发某种异常。在下面的代码中,您可以看到我们添加了一个带有“TestMethod”操作的“TestingController”,我们在其中引发了除以零的异常。
public class TestingController : Controller { public ActionResult TestMethod() { int x = 0; x /= x; //Above line leads to DivideByZeroException return View(); } }
所以,如果您执行上述操作,您将最终得到如下图所示的错误。
步骤 2:- 创建一个错误视图
现在,一旦错误被上述三种方法之一捕获,我们将显示一个错误页面。所以,让我们创建一个名为“Error”的简单视图,如下图所示。
现在我们有了错误,也有了错误视图,是时候进行演示了,使用所有三种方法。所以,首先让我们从“OnException”开始,即在同一个控制器中跨操作重用异常代码。
步骤 3:- 在同一控制器内的操作之间重用(OnException)
要实现异常处理,请转到“TestingController”并重写“OnException”方法,如下面的代码所示。当“TestingController”中的任何操作发生错误时,将执行此方法。
如下面的代码所示,在“filterContext”对象的 result 属性中设置了视图名称,即“Error”。
protected override void OnException(ExceptionContext filterContext) { Exception e = filterContext.Exception; //Log Exception e filterContext.Result = new ViewResult() { ViewName = "Error" }; }
现在,如果您尝试从“TestController”调用“TestMethod”,您应该会看到如以下图所示的“Error”视图。
“OnException”方法有助于为特定控制器提供错误处理,但如果我们想在任何控制器和任何操作之间重用异常逻辑怎么办?这就是“FilterConfig”的使用方式,这是下一项。
步骤 4:- 在任何操作和任何控制器之间重用(FilterConfig)
在 Web.Config 中,只需按如下方式启用自定义错误。
<customErrors mode="On">
在 App_Start 文件夹中打开 FilterConfig.cs,并确保将 HandleErrorAttribute 添加到 GlobalFilterCollection 中。
全局级别的 HandleErrorAttribute 确保所有控制器中每个操作引发的异常都将被处理。
注意:如果您想知道如何使“HandleErrorAttribute”特定于控制器或操作,请单击此处,详细了解 ASP.NET MVC 中的异常处理。
如果您执行控制器,您应该会得到与步骤 3 中所示相同的错误页面。
步骤 5:- 项目级别的错误处理:- Application_Error
为了处理整个 MVC 项目的错误,我们可以监听 global.asax 文件中的“Application_Error”事件,并在其中编写错误处理逻辑。
就是这样。使用所有 3 种方法在 MVC 中进行异常处理的简单演示。
注意:我们为我们的循序渐进系列上传了一篇支持文章。它详细解释了 ASP.NET MVC 中的异常处理。如果您愿意阅读更多相关内容,请单击此处。
实验 21:- MVC 区域
什么是区域,为什么需要区域?
在 ASP.NET MVC 中,我们有一个区域的概念,利用它可以将我们的系统分解成模块,并以更好的方式组织我们的项目。
假设我们有一个系统,包含两个模块:客户和订单处理。通常,当我们创建 Asp.Net MVC 项目时,我们的结构包含 3 个文件夹 – Controllers、Model 和 Views。所以项目结构将如下所示。
![]() | 正如您所见,没有任何组织。当涉及到代码管理时,这将非常困难。 |
现实世界中区域的例子
![]() | 国家被划分为州,以简化开发和管理。 |
就像现实世界一样,我们在 Asp.Net MVC 中使用区域的概念将单个系统分解成模块。一个区域通过逻辑分组控制器、模型和视图来代表一个模块。
如何使用区域?
步骤 1:- 使用 MVC 模板添加区域。
要添加区域,请右键单击您的项目,然后选择 Add>>Area,如下图所示。
步骤 2:将相关文件推送到相关区域
将所有相关文件放入各自的区域,如下图所示。在下图您可以看到我创建了两个区域“Customer”和“Order”。每个区域都有自己的视图、控制器和模型。
注意:- 区域是逻辑分组,不是物理分组,因此每个区域不会创建单独的 DLL。
区域与文件夹有什么区别?
有人可能会问,为什么在可以使用文件夹来分解系统时,我们还要使用区域来分解系统。简单来说,这个问题的答案是“避免大量手动工作”。
为了使用简单的文件夹来实现相同的功能,您需要执行以下操作。
- a. 每个模块一个文件夹。
- b. 每个模块文件夹内有三个文件夹,分别命名为 Controller、Models 和 Views。
- c. 每个模块一个 Web.Config 文件(用于存储与该模块相关的设置)。
- 手动创建模块结构。
- 为每个模块的控制器创建自定义路由。
- 为每个模块创建自定义视图引擎,它将在自定义位置而不是预定义位置(Views/{Controllers} 或 Views/Shared)搜索视图。
默认视图搜索
自定义视图搜索
第六天讲什么?
最后,您可以在我的C# 和 MVC 培训视频中观看关于 WCF、Silver light、LINQ、WPF、设计模式、Entity framework 等各个部分的讲解。无论如何,不要错过我在www.questpond.com 上的.NET/C# 面试题和答案书籍。
有关ASP.NET、设计模式、WCF、MVC、BI、WPF等各种主题的技术培训,请联系SukeshMarla@gmail.com或访问www.sukesh-marla.com
您是MVC新手吗?
如果您是完全的新手,我建议从以下 4 个大约 10 分钟的视频开始,这样您可以快速进入 MVC。
实验 1:一个简单的 Hello World ASP.NET MVC 应用程序
实验 2:在这个实验中,我们将看到如何使用 ViewData 在控制器和视图之间共享数据
实验 3:在这个实验中,我们将创建一个简单的客户模型,填充一些数据,并在视图中显示它
实验 4:在这个实验中,我们将创建一个带有视图验证的简单客户数据录入屏幕
从 MVC 5 开始
如果您想从 MVC 5 开始,请从下面的视频“2 天学会 MVC 5”开始。
不要错过带答案的 MVC 面试题
在这个 7 天系列中,我发布的每个实验都附带一篇单独的文章,讨论在面试中会问到的重要 MVC 面试题。到目前为止,我已经收集了 60 个重要问题及精确答案,您可以在这里查看。
如需进一步阅读,请观看以下面试准备视频和分步视频系列。