7天学会MVC——第2天

原文地址
这是我在CodeProject上面看到的一篇文章,这里是我翻译的中文版,如果有任何翻译不恰当之处,还望各位不吝赐教!

下载 ViewBag_and_ViewData.zip
下载 Strongly_Typed_View.zip
下载 View_Model.zip
下载 Collections.zip

本文将带领大家在7天内逐步学习MVC5——第2天

简介

  我们相信,在此之前你已经顺利完成了第一天的内容。
  第一天主要聚焦在以下方面:
  ● 为何选择MVC?
  ● ASP.NET WebForms和ASP.NET MVC的对比
  ● 理解Controllers和Views

注意
  如果你没有完成前一天的内容,请务必先完成。我们的目标是在今天结束的时候,用最好的方法和现代方法创建一个小的MVC项目。每一个新的实验中,我们要么在之前实验的基础之上添加新功能,要么让之前的实验更完善。

从Controller向View传递数据

  在实验2中创建的视图非常的静态化,在实际场景中视图一般会展示一些动态数据。在接下来的实验中,我们将会在视图中展示一些动态数据。
  视图从Controller中以Model的形式接收数据。

Model
  在ASP.NET MVC中model代表业务数据。

实验3-使用ViewData

  ViewData是一种数据字典,包含了再Controller和View之间传递的数据。Controller将会向这个数据字典中添加条目,View将会从中读取。我们来做个示例吧。

Step 1-创建Model类

  在Model文件夹中创建新类,命名为Employee,如下:

public class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Salary { get; set; }
}

Step 2-在Controller中获取Model

  创建在GetView方法中Employee对象,如下:

Employee emp = new Employee();
emp.FirstName = "Sukesh";
emp.LastName="Marla";
emp.Salary = 20000;

  确保添加了对Employee命名空间的引用,否则就只能用Employee的权限定名了。

using WebApplication1.Models;

Step 3-创建ViewData,返回View

  在Employee对象中储存ViewData,如下:

ViewData["Employee"] =emp;
return View("MyView");

Step 4-在View中展示ViewData数据

  打开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>

Step 5-测试输出

  按下F5,测试程序。
  


   7天学会MVC——第2天_第1张图片
  

关于实验3的讨论

  在Razor代码中,有花括号(“{“和”}”)与没有花括号有什么区别?
  在这个实验中,@emp.FirstName可以用如下代码片替换:

@{ Response.Write(emp.FirstName);
}

  没有花括号的@语句,仅仅展示后面的变量或者表达式的值。
  
  为什么需要类型转换?
  ViewData内存存放的是object类型,每次往里面添加新值的时候都会对其进行装箱操作,转换成object类型。因此每一次尝试提取ViewData中数据的时候,都需要进行拆箱操作。

  @emp.FirstName @emp.LastName的含义是什么?
  展示First Name,和Last Name,并且在First Name后面添加一个空格。

  我们可以只写一个@关键字来完成同样的工作吗?
  可以,这样写的语法应该为@(emp.FirstName+” “emp.LastName)

  为什么在Controller中创建的Employee的数据是写死的?
  只是为了展示的目的,在实际情况中,我们将会从数据库或者WCF或者Web Service或者其他的地方获取数据。

  关于Database Logic/Data Access Layer和Business Layer
  ● DAL是在ASP.NET MVC中没有提到的一层,它一直存在,但是并没被包括在MVC的定义中。
  ● BL在先前解释过了,是Model的一部分。
  ● 完整的MVC结构:
  

实验4-使用ViewBag

  ViewBag只是ViewData的一个语法糖衣而已,ViewBag使用C#4.0的动态特性,将ViewDate变得动态。
  ViewBag内部使用ViewData。

Step 1-创建ViewBag

  继续实验3,将第3步的代码替换成如下:

ViewBag.Employee = emp;

Step 2-在View中展示Employee数据

  将第4步改为如下代码:

@{
    WebApplication1.Models.Employee emp = (WebApplication1.Models.Employee)
        ViewBag.Employee;
}
Employee Details

Employee Name: @emp.FirstName @emp.LastName 

Employee Salary: @emp.Salary.ToString("C")

Step 3-测试输出

  按下F5,测试程序:


7天学会MVC——第2天_第2张图片

关于实验4的讨论

  我们能用ViewData传递数据,然后用ViewBag获取数据吗?
  可以,反之亦然。正如我之前所说,ViewBag只是ViewData的语法糖衣而已。

ViewData和ViewBag的问题

  ViewData和ViewBag是在Controller和View之间传递数据的不错的选择,但是在实际的工程中,采用它们之中任何一个都不是好的选择。让我们来讨论关于ViewData和ViewBag的一些劣势吧:

  效率问题
  ViewData内部的数据为object类型,我们需要在使用之前将其转换为正确的类型,在效率方面带来额外的天花板。

  非类型安全,没有编译时报错
  如果我们尝试将值转换成错误的类型的时候,或者当获取值的时候使用了错的key,我们会获得运行时错误。作为一个好的编程惯例,错误应该在编译的时候被处理掉。

  在发送的数据和接收的数据之间没有合适的连接
  作为开发人员,我个人认为这是一个主要问题。

  在MVC中,Controller和View彼此之间是松散连接的。Controller对于View中发生了什么完全不清楚,反之亦然。

  在Controller中我们可以传递一个或者更多的ViewData/ViewBag值,现在,当开发人员书写一个View的时候,他/她需要记住从Controller传递来的是什么。如果Controller和View的开发人员不一样,这将会变得更加困难。彼此之间毫无知觉,会导致很多运行时问题,也会降低开发效率。
  

实验 5-理解强类型Views

  引起ViewData和ViewBag三个问题的原因就是数据类型,ViewData内部数据的类型为object.

  如果我们能够确定在Controller和View之间传递的数据类型,这些问题都会得到解决,这也就是强类型的Views出现的原因。

  让我们来做个示例吧,这次,我们将View的要求提升到另外一个水准。如果Salary大于15000,将会用黄色背景来展示,反之用绿色背景展示。

Step 1-将View变成强类型View

  在View顶部添加如下代码:

@model WebApplication1.Models.Employee

  上面的代码将我们的View变成Employee的强类型视图。

Step 2-展示数据

  现在在View里,仅仅只需要敲下@Model.,智能感应就会获得Model类(Employee)的属性。


7天学会MVC——第2天_第3张图片

    
  写下下列代码,展示数据:

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>
}

Step 3-从Controlle Action 方法传递Model数据

  改变action方法里面的代码如下:

Employee emp = new Employee();
emp.FirstName = "Sukesh";
emp.LastName="Marla";
emp.Salary = 20000; 
return View("MyView",emp);

Step 4-测试输出


这里写图片描述

  

关于实验5的讨论

  在视图中,是否每次都需要输入类的全部限定名称?
  不,我们可以引入一个using声明。

@using WebApplication1.Models
@model Employee

  是否所有视图都必须是强类型的?我们可以使用ViewData或者ViewBag吗?
  作为最佳做法,我们最好将所有视图作为强类型视图。
  
  我们可以将视图作为多个model的强类型视图吗?
  不可以。在实际工程里面,我们经常在同一个视图中展示多个model的时候卡住,解决这个问题的方法会在下一个试验中讨论。
  

理解ASP.NET MVC 中的View Model#

  在实验5中我们违反了MVC原则。根据MVC,V指视图,应该是纯UI,不应该在里面包含任何逻辑。我们做的一下三点,完全违反了MVC的原则:
  ● 将FirstName和LastName附在一起,并且作为全名展示。——逻辑
  ● 展示Salary的时候附上了货币符号。——逻辑
  ● 根据Salary的值,用不同的颜色展示Salary。简单地说就是根据Salary的值,更改了HTML元素的外观。——逻辑

  除了这三点之外,还有一个值得讨论的问题。
  前面说到,我们在往同一个View中展示多种类型的数据的时候遇到了问题。例如:展示Employee数据的时候同时展示用户名(User’s Name)。

  用以下两种方法中的任意一种,都可以达成目的:
  1. 在Employee类中添加UserName属性。但是,每次我们想展示view中的新数据的时候,就为Employee类型添加似乎不符合逻辑,这些属性跟Employee也许有关,也许无关。这也同样违反了SOLID的SRP原则(Single Response Principle)。
  2.使用ViewBag或者ViewData - 但是我们已经讨论过了这种方法的问题了。
  

解决方案:ViewModel

  ViewModel是在ASP.NET MVC中没有提到的一层,它在Model层和View层中间,扮演着View数据容器的角色。

  ViewModel和Model的区别
  Model是特定的业务数据,它根据业务和数据库结构创建;ViewModel是特定的视图数据,它根据视图创建。

  它到底如何工作
  很简单:
  ● Controller处理用户交互逻辑,简单地说就是处理用户请求。
  ● Controller获得一个或多个model数据。
  ● Controller将会决定该请求的最佳匹配视图。
  ● Controller将会根据从View请求获得的Model数据实例化ViewModel对象。
  ● controller将会通过ViewData/ViewBag/Strongly Typed View向视图传递ViewModel数据。
  ● Controller返回视图。

  View和ViewModel如何连接呢?
  View将会是ViewModel的一个强类型视图。

  Model和ViewModel如何连接呢?
  Model和ViewModel彼此之间应该是相互独立的,Controller会根据一个或多个Model数据来实例化ViewModel对象。
  让我们做一个小实验来更好地理解它。

实验 6-实现View Model

Step 1-创建文件夹

  在工程中创建名叫ViewModels的新文件夹。

Step 2-创建EmployeeViewModel

  为了达成目的,让我们列出对View的所有要求:
  1.First Name 和Last Name应该在展示之前就附加好了。
  2.Amount应该和货币符号一起展示。
  3.Salary应该用不同的颜色展示(根据salary的值不同,颜色不同)。
  4.当前User Name应该一同在View中展示。

  在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。
    

Step 3-在View中使用View Model

  在实验5中,我们的View是Employee类型的强类型视图,改成EmployeeViewModel类型的强类型视图。

@using WebApplication1.ViewModels
@model EmployeeViewModel

Step 4-在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>

Step 5-创建并传递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);
}

Step 6-测试输出

  按下F5按钮,测试输出。


7天学会MVC——第2天_第4张图片

  同实验5的输出完全一样,但是这次View里面没有包含任何逻辑。

关于实验6的讨论

  是否每一个Model都需要一个View Model?
  不是,是每一个View都有它对应的ViewModel。

  让Model和ViewModel之间存在一些联系是否是一个好的做法?
  不,最好的做法就是,Model和ViewModel之间完全独立。

  我们是否总是需要创建ViewModel?如果View不包含任何展示的逻辑,而且只需要将Model数据原样展示出来的时候该怎么办?
  我们应该总是创建ViewModel,每一个View都应该拥有它自己的ViewModel,即使ViewModel包含的属性和Model一样。

  我们来讨论一个场景,View没有包含展示逻辑,View需要将Model数据原样展示。假设这次我们没有创建ViewModel。

  那么,问题来了,如果在将来的需求中,我们被要求在UI中展示一些新数据或者我们被要求加入一些展示逻辑,我们也许需要一个全新的UI来达成要求了!

  因此,我们从一开始就做好准备,创建ViewModel是一个更好的选择。在这个情况里,刚开始的ViewModel和Model几乎一模一样。

实验7-包含集合的View

  在这个实验中我们将在View中展示Employee的List。

Step 1-修改EmployeeViewModel类

  从EmployeeViewModel中移除UserName属性。

public class EmployeeViewModel
{
    public string EmployeeName { get; set; }
    public string Salary { get; set; }
    public string SalaryColor { get; set; }
}

Step 2-创建集合ViewModel

  在ViewModel文件夹里面新建类EmployeeListViewModel,如下:

public class EmployeeListViewModel
{
    public List<employeeviewmodel> Employees { get; set; }
    public string UserName { get; set; }
}

Step 3-修改强类型View的类型

  
  让MyView.cshtml成为EmployeeListViewModel的强类型视图。

@using WebApplication1.ViewModels
@model EmployeeListViewModel

Step 4-在View中展示所有Employee

<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>

Step 5-为Employee创建业务层

  在这个实验中,我们将会把我们的工程带入到下一个水准层次。我们将会添加业务层,创建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;
    }
}

Step 6-从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);
}

Step 7-执行并测试输出

  按下F5,执行程序。


7天学会MVC——第2天_第5张图片

关于实验7的讨论

  View可以作为List的强类型视图吗?
  可以。

  为什么单独新建EmployeeListViewModel类,不直接让View作为List的强类型视图呢?
  如果我们直接使用List而不是EmployeeListViewModel,这样做有两个问题:
  1.未来的展示逻辑的管理问题。
  2.UserName不是跟Employee个体关联的,跟View关联的。

  为什么将UserName属性从EmployeeViewModel 转移到EmployeeListViewModel中?
  UserName不是Employee的属性,所以不需要再EmployeeViewModel中包含UserName属性。

总结

  我们结束了第2天的内容,在第3天我们将会把我们的工程带入另一个版本。

你可能感兴趣的:(mvc,asp-net,Net-MVC,学习MVC)