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 : @[email protected] <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 中展示不同类型的数据。比如:显示当前登录的用户名称和雇员数据。

我们可以使用如下两种方式实现这个问题:

  1. 向 Employee 类增加一个 UserName 属性。每一次我们想要在视图中展示一个新数据,我们就像 Employee 类中增加一个属性。这似乎是不合理的,这个属性也许和 Employee 没有关联。这也违反了 SOLID 的 SRP 准则。
  2. 运用 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 的所有需求。

  1. First Name 和 LastName 需要合并展示,所以在展示前它们应该是合并的。
  2. 使用货币形式来显示 Amount。
  3. 不同的 Salary 展示出不同的颜色。
  4. 当前的 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 类,会引起两个问题。

  1. 未来也许会出现展示逻辑的需求。
  2. UserName 属性。因为 UserName 和 Employees 是没有关联的。它是与一个完整的 View 相关联。

为什么我们要从 EmployeeViewModel 中移除 UserName 属性,然后把它作为EmployeeListViewModel 中的一部分呢?

UserName 对所有雇员都是一样的,如果将 UserName 的属性保留在 EmployeeViewModel 中就会增加了冗余代码,也会为增加数据的传输额外内存空间。

5. 结语

我们已经完成了第 2 天的 MVC 学习。在第 3 天中我们将使得项目进入下一个阶段。

让我一起在学习中尽情徜徉吧!

原文地址:Learn MVC Project in 7 days

7 天玩转 ASP.NET MVC — 第 1 天

本文系 OneAPM 工程师编译整理。OneAPM 是中国基础软件领域的新兴领军企业,能帮助企业用户和开发者轻松实现:缓慢的程序代码和 SQL 语句的实时抓取。想阅读更多技术文章,请访问 OneAPM 官方博客

时间: 2024-10-14 04:33:43

7 天玩转 ASP.NET MVC — 第 2 天的相关文章

7 天玩转 ASP.NET MVC — 第 7 天

目录 第 1 天 第 2 天 第 3 天 第 4 天 第 5 天 第 6 天 第 7 天 0. 前言 今天是开心的一天.因为我们终于来到了系列学习的最后一节.我相信你喜欢之前的课程,并从中学到了许多. 1. Lab 32 - 让项目有组织性 这个实验确切地讲无关任何新的功能.它只是使项目更有结构性和系统化. 第一步:创建解决方案文件夹 右击解决方案,然后选择 Add -> New Solution Folder. 将文件夹的名称改为「View And Controller」.现在重复这个步骤,创

7 天玩转 ASP.NET MVC — 第 1 天

0. 前言正如标题「7 天玩儿转 ASP.NET MVC」所言,这是个系列文章,所以将会向大家陆续推出 7 篇.设想一下,一天一篇,你将从一个愉快的周一开始阅读,然后在周末成为一个 ASP.NET MVC 开发者,这很酷吧! 7 天玩儿 转 ASP.NET MVC — 第 1 天 第一天是热身运动,这篇我们将围绕 Controller 和 Views 实践两个 Labs.在每个 Lab 之中都伴随着一些 Question 和 Answer.所以文章的主体框架是 Lab 和 Q&A. 7 天玩儿

7 天玩转 ASP.NET MVC — 第 5 天

目录 第 1 天 第 2 天 第 3 天 第 4 天 第 5 天 第 6 天 第 7 天 0. 前言 欢迎来到第五天的学习.希望第一天到第四天的学习,你都是开心的. 1. Lab 22 - 增加 Footer 在这个实验中,我们将会向 Employee 页面添加 Footer.本次实验的目标是理解分部视图(Partial Views). 什么是「Partial Views」? 逻辑上讲,分部视图(Partial Views) 是一个可重用的视图,它不会被直接显示.它会被其它视图所包含,然后作为该

7 天玩转 ASP.NET MVC — 第 4 天

目录 第 1 天 第 2 天 第 3 天 第 4 天 第 5 天 第 6 天 第 7 天 0. 前言 欢迎来到第四天的 MVC 系列学习中.如果你直接开始学习今天的课程,我强烈建议你先完成之前的学习内容再来到这里. 1. Lab 15 - 认证错误的保留值 在 Lab 13 中,我们介绍了服务器端的认证,并且在 Lab 14 中,我们通过添加自定义认证的方式将其提示到一个新的层级. 我强烈建议你再回顾一下 Lab 14.再次执行应用,并且能够很好地理解代码以及输出. 在 Lab 15 中,我们将

玩转Asp.net MVC 的八个扩展点

MVC模型以低耦合.可重用.可维护性高等众多优点已逐渐代替了WebForm模型.能够灵活使用MVC提供的扩展点可以达到事半功倍的效果,另一方面Asp.net MVC优秀的设计和高质量的代码也值得我们去阅读和学习. 本文将介绍Asp.net MVC中常用的八个扩展点并举例说明. 一.ActionResult ActionResult代表了每个Action的返回结果.asp.net mvc提供了众多内置的ActionResult类型,如:ContentResult,ViewResult,JsonRe

7 天玩转 ASP.NET MVC — 第 3 天

目录 第 1 天 第 2 天 第 3 天 第 4 天 第 5 天 第 6 天 第 7 天 0. 前言 我们假定你在开始学习时已经阅读了前两天的学习内容.在第 2 天我们完成了关于显示 Employees 列表的项目. 在第三天,我们将会通过介绍数据访问层和数据入口将它升级到一个新的层次. 1. 数据访问层 在真实场景的项目中,如果没有 Database,那么这个项目是未完成的.在我们的项目中,我们还没有谈到数据库.第三天的首个 Lab 将会学习数据库和数据库层. 这里我们将使用 SQL Serv

7 天玩转 ASP.NET MVC — 第 6 天

目录 第 1 天 第 2 天 第 3 天 第 4 天 第 5 天 第 6 天 第 7 天 0. 前言 欢迎来到第六天的 MVC 系列学习中.希望你在阅读此篇文章的时候,已经学习了前五天的内容,这也是第六天学习的前提条件. 1. Lab 27 — 添加批量上传选项 在这个实验中,我们将会创建一个选项,用于从 CSV 文件中上传多个 Employees. 我们将会做两件事. 1. 学会如何运用文件上传控件. 异步控制器. 第一步:创建 FileUploadViewModel 在 ViewModels

7天玩转 ASP.NET MVC

在开始时请先设置firefox中about:config中browser.cache.check_doc_frequecy设置为1,这样才能在关闭浏览器时及时更新JS 第一.二天的内容与之前的重复,这里不再重复 弱类型ViewData 中的数据类型是Object.所以我们在使用之前需要进行正确的类型转换,没有类型安全 public ActionResult GetView() { Employee emp = new Employee(); emp.FirstName = "Sukesh&quo

ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL

http://www.cnblogs.com/John-Connor/archive/2012/05/03/2478821.html 引言-- 在初级篇中,我们介绍了如何利用基于ASP.NET MVC的Web程序中的Global文件来简单的重写路由.也介绍了它本身的局限性-依赖于路由信息中的键值对: 如果键值对中没有的值,我们无法将其利用凑出我们想要的URL表达式. 初级篇传送门:使用Global路由表定制URL   在进阶篇中,我们将介绍ASP.NET 路由相关类的基类-抽象类RouteBas