65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (62投票s)

2014年2月13日

CPOL

19分钟阅读

viewsIcon

180664

顾名思义,这篇文章是关于学习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 的内容)。

它看起来像一只鸭子,叫声像一只鸭子,但它不是一只鸭子。它看起来像一个模型,具有像模型一样的属性,但它不是完全的模型。


图片来自 http://tomdalling.com

视图模型的优势

  • 可重用性:- 现在我的胶水代码已经进入了一个类。我可以轻松地在任何其他 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。所以项目结构将如下所示。

正如您所见,没有任何组织。当涉及到代码管理时,这将非常困难。
侧边图片是拥有 2 个模块的项目结构,想象一下当一个系统中有很多模块的情况。

现实世界中区域的例子

国家被划分为州,以简化开发和管理。

就像现实世界一样,我们在 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 个重要问题及精确答案,您可以在这里查看。

如需进一步阅读,请观看以下面试准备视频和分步视频系列。

© . All rights reserved.