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

目录

0. 前言

今天是开心的一天。因为我们终于来到了系列学习的最后一节。我相信你喜欢之前的课程,并从中学到了许多。

1. Lab 32 — 让项目有组织性

这个实验确切地讲无关任何新的功能。它只是使项目更有结构性和系统化。

第一步:创建解决方案文件夹

右击解决方案,然后选择 Add -> New Solution Folder。

将文件夹的名称改为「View And Controller」。现在重复这个步骤,创建多个相似的文件夹,分别命名为「Model」,「ViewModel」,「Data Access Layer」。

第二步:创建数据访问层项目

右击 Data Access Layer 文件夹,然后创建一个新的类库项目,命名为「DataAccessLayer」。

第三步:创建业务层和业务实体层项目

在 Model 文件夹下创建两个类库项目,分别命名为「BusinessLayer」 和 「BusinessEntities」。

第四步:创建 ViewModel 项目

在 ViewModel 文件夹下创建一个新的类库项目,命名为「ViewModel」。

第五步:添加引用

首先右击每一个项目,然后选择 Add -> Reference,选择如下引用。

  1. 对于 DataAccessLayer,选择 BusinessEntities。
  2. 对于 BusinessLayer,选择 DataAccessLayer 和 BusinessEntities。
  3. 对于 MVC Web Application,选择 BusinessLayer,BusinessEntities 和 ViewModel。
  4. 对于 BusinessEntities,选择 System.ComponentModel.DataAnnotations。

第六步:设置项目

  1. 从 MVC 项目中的 DataAccessLayer 文件夹中复制 SalesERPDAL.cs 文件到新创建的 Data Access Layer 类库项目中。

  1. 从 MVC 项目中移除 DataAccessLayer 文件夹。
  2. 从 MVC 项目的 Model 文件夹下复制 Employee.cs,UserDetails.cs 和 UserStatus.cs 文件到新创建的 BusinessEntities 类库项目中。
  3. 从 MVC 项目的 Model 文件下复制 EmployeeBusinessLayer.cs 文件到新创建的 BusinessLayer 类库项目中。
  4. 从 MVC 项目中移除 Model 文件夹。
  5. 从 MVC 项目的 ViewModels 文件夹下复制所有类到新创建的 View Model 类库项目中。
  6. 从 MVC 项目中移除 ViewModels 文件夹。
  7. 将 MVC 项目,即 WebApplication1 移到「View And Controller」解决方案文件夹。

第七步:Build 项目

选择菜单栏的 Build -> Build Solution。你将会得到如下的错误信息。

第八步:解决错误

  1. 向 ViewModel 项目中添加 System.Web 引用。
  2. 在 DataAccessLayer 和 BusinessLayer 项目中运用 Nuget Manager,安装 Entity Framework。(如果你对 Nuget Manager 感到困惑,建议你看一下第 3 天的课程)

注:业务层需要引用 Entity Framework 是因为 BusinessLayer 直接与 DataAccessLayer 相关联。在一个正确的架构中,业务层不应该与数据访问层直接关联。我们可以通过 Repository 来完成这个目的。

  1. 从 MVC 项目中移除 EntityFramework。

步骤如下。

  • 右击 MVC 项目,选择「Manage Nuge Packages」选项。
  • 在左侧区域的「Manage Nuget Packages」对话框下选择「Installed Packages」。
  • 右击区域将会显示之前下载过的所有包。选择 EntityFramework,然后点击 Uninstall。

第九步: Build 解决方案

你将会看到如下错误。

第十步:解决错误

现在,我们在 MVC 项目中既没有 SalesERPDAL 引用,也没有 Entity Framework 引用。添加这些引用不是一个最佳实践。作为最佳实践,控制器不应该与数据访问层直接相关联。

  • 在 DataAccessLayer 项目中创建一个新的类,命名为 DatabaseSettings,它有一个静态方法,命名为 SetDatabase。
    using System.Data.Entity;    using WebApplication1.DataAccessLayer;    namespace DataAccessLayer    {        public class DatabaseSettings        {            public static void SetDatabase()            {                Database.SetInitializer(new    DropCreateDatabaseIfModelChanges<SalesERPDAL>());<saleserpdal>            }        }       }
  • 在 BusinessLayer 项目中创建一个新的类,命名为 BusinessSettings,它有一个静态方法,命名为 Setbusiness。

  1. using DataAccessLayer;
  2. namespace BusinessLayer
  3. {
  4.    public class BusinessSettings
  5.    {
  6.        public static void SetBusiness()
  7.        {
  8.            DatabaseSettings.SetDatabase();
  9.        }
  10.    }
  11. }
  • 在 Global.asax 中运用语句来解决错误,并且移除 Database.SetInitializer 语句。触发 BusinessSettings.SetBusiness 函数。
using BusinessLayer;......BundleConfig.RegisterBundles(BundleTable.Bundles);BusinessSettings.SetBusiness();

再次 Bulid 应用,这一次将会成功。

Lab 32 的 Q&A

什么是解决方案文件夹?

Solution 文件夹只是逻辑文件夹。实际上,它不会在物理硬盘上被创建。它的目的只是为了让解决方案更加系统化。

2. Lab 33 — 创建单页应用 — Part 1 — 设置

现在,我们将不会对已存在的控制器和视图做出改变。我们将会为此实验创建一个全新的控制器和视图。做这些步骤的理由是:

  1. 使已存在的项目不受影响,因此你可以将之前的版本和单一页版本进行对比,更好地学习。
  2. 实现并理解 ASP.NET MVC 中的另一个概念,即 Areas。

正如我所说的,我们将会创建新的控制器,视图和视图模型。

只有以下组件会被重复利用。

  • 已存在的 Business Layer。
  • 已存在的 Data Access Layer。
  • 已存在的 Business Entities。
  • Authentication 和异常过滤器。
  • FooterViewModel。
  • Footer.cshtml。

第一步:创建一个新的 Area

右击项目,然后选择 Add -> Area。一个对话框将会弹出,输入名称为 SPA,然后点击 Add。

它将在项目中创建一个新的文件夹架构,如下所示。

很明显,我们并不需要 Area 下的 Model 文件夹,删除即可。

什么是 Areas?

Areas 是实现 ASP.NET MVC 项目模块化的一种简单方式。

每一个项目都由多个模块组成。例如:账户模块,顾客关系模块,付款模块,等等。

在传统的应用开发风格中,我们经常使用「Folders」来达到这个目的。我们在一个单独项目中创建多个文件夹。每一个文件夹代表一个模块。我们会把各自模块的文件放在各自的文件夹中。

当使用 ASP.NET MVC 时,这种自定义文件夹将会遇到大问题。

让我们来讨论下在 ASP.NET MVC 中,运用简单的文件夹来实现模块。

  • DataAccessLayer,BusinessLayer,BusinessEntities 和 ViewModels 不会产生任何问题。他们仅仅是简单的类,所以可以被放置到任意地方。
  • 我们不能将控制器随意放置。它必须放置在 Controller 文件夹下。但是这不会成为一个大问题,因为从 MVC 4 开始,控制器的位置限制就已经被舍弃了。现在我们可以将其放置到任意想要放置的地方。
  • 不幸的是,对于视图是不可行的。所有的视图都必须放置在「~/Views/ControllerName」或者「~/Views/Shared」文件夹下。

第二步:创建所需的 ViewModels

在 ViewModel 类库项目中创建一个新的文件夹,命名为 SPA,然后创建一个 ViewModel,命名为 MainViewModel。

using WebApplication1.ViewModels;namespace WebApplication1.ViewModels.SPA{    public class MainViewModel    {        public string UserName { get; set; }        public FooterViewModel FooterData { get; set; }//New Property    }}

第三步:创建 Index 行为方法

在 MainController 中引用如下语句。

using WebApplication1.ViewModels.SPA;using OldViewModel=WebApplication1.ViewModels;

在 MainController 中创建一个新的行为方法,命名为 Index。

public ActionResult Index(){    MainViewModel v = new MainViewModel();    v.UserName = User.Identity.Name;    v.FooterData = new OldViewModel.FooterViewModel();    v.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value    v.FooterData.Year = DateTime.Now.Year.ToString();    return View("Index", v);}


如你所见,为 WebApplication1.ViewModels 命名空间增加了一个 OldViewModels 别名。现在,我们可以使用
OldViewModel.ClassName,而不是 WebApplication1.ViewModels.ClassName。

不指定别名会导致歧义错误。在命名空间 WebApplication1.ViewModels.SPA 和 WebApplication1.ViewModels 中,存在相似的类。

第四步:创建 Index 视图

创建一个与上述 Index 方法相联系的视图。


  1. @using WebApplication1.ViewModels.SPA
  2. @model MainViewModel
  3. <!DOCTYPE html>
  4. <html>
  5. <head>
  6.    <meta name="viewport" content="width=device-width" />
  7. <title>Employee Single Page Application</title>

第五步:执行并测试应用

按下 F5,然后执行应用。完成登录操作,然后导航到 MainController 中的 Index 行为。

Lab 33 的 Q&A

为什么在控制器名称之前需要 SPA 关键字?


我们向 ASP.NET MVC 中添加 Area 时,Visual Studio 就会创建一个文件,命名为
[AreaName]AreaRegistration.cs,它包含一个类,这个类定义了 AreaName 属性和 RegisterArea
方法,该方法用于为 Area 注册路由信息。

在我们的例子中,你可以发现名称为 SpaAreaRegistration.cs 文件,它被放置在「~/Areas/Spa」文件夹下。SpaAreaRegistration 类的 RegisterArea 方法包含如下代码。

context.MapRoute(                "SPA_default",                "SPA/{controller}/{action}/{id}",                new { action = "Index", id = UrlParameter.Optional });

这就解释了为什么我们需要在控制器名称前添加 SPA 关键字。

SpaAreaRegistration 类中的 RegisterArea 方法如何被触发?

打开 Global.asax 文件,Application_Start 的第一行如下。

AreaRegistration.RegisterAllAreas();

RegisterAllAreas 方法查找到应用中所有来自于 AreaRegistration 的类型,并调用它们的每一个 RegisterArea 方法。

我们可以不使用 SPA 来触发 MainController 动作吗?

让我们简化一下问题:URL 为「localhost:8870/Main/Index」还会起作用吗?


案是肯定的。AreaRegistration 类创建一个新的路由,但是不会删除其它路由。路由在 RouteConfig
类中定义,仍然起作用。正如我之前所说的那样,控制器的位置没有限制。因此它仍能起作用,但是输出不会被正确地呈现,因为它将不能查找到视图。我建议你执
行一下应用,试一试。

3. Lab 34 — 创建单页应用 — Part 2 — 展示 Employees

第一步:为展示已存在的 Employees 创建 ViewModel

在 ViewModel 类库的 SPA 文件夹下创建两个新的 ViewModel 类,命名为 EmployeeViewModel 和 EmployeeListViewModel。

namespace WebApplication1.ViewModels.SPA{    public class EmployeeViewModel    {        public string EmployeeName { get; set; }        public string Salary { get; set; }        public string SalaryColor { get; set; }    }}
namespace WebApplication1.ViewModels.SPA{    public class EmployeeListViewModel    {        public List<employeeviewmodel> Employees { get; set; }    }}

注:两个 ViewModel 实际上都是 Non-Spa 应用的 ViewModel 复制品。唯一的区别是不需要 BaseViewModel 了。

第二步:创建 EmployeeList Index

在 MainController 下创建一个新的行为方法,命名为 EmployeeList。


  1. public ActionResult EmployeeList()
  2. {
  3.    EmployeeListViewModel employeeListViewModel = new EmployeeListViewModel();
  4.    EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();
  5.    List<employee> employees = empBal.GetEmployees();
  6.    List<employeeviewmodel> empViewModels = new List<employeeviewmodel>();
  7.    foreach (Employee emp in employees)
  8.    {
  9.        EmployeeViewModel empViewModel = new EmployeeViewModel();
  10.        empViewModel.EmployeeName = emp.FirstName + " " + emp.LastName;
  11.        empViewModel.Salary = emp.Salary.Value.ToString("C");
  12.        if (emp.Salary > 15000)
  13.        {
  14.            empViewModel.SalaryColor = "yellow";
  15.        }
  16.        else
  17.        {
  18.            empViewModel.SalaryColor = "green";
  19.        }
  20.        empViewModels.Add(empViewModel);
  21.    }
  22.    employeeListViewModel.Employees = empViewModels;
  23.    return View("EmployeeList", employeeListViewModel);
  24. }

注:HeaderFooterFilter 不再需要。

第三步:创建 AddNewLink 分部视图

这次我们不能运用之前的 AddNewLink 分部视图,因为之前的标签设定会导致全部的刷新。我们的目标是创建一个「Single Page Application」,因此我们不应该有全部的刷新。

在「~/Areas/Spa/Views/Main」文件夹下创建一个新的分部视图,命名为 AddNewLink.cshtml。

<a href="#" onclick="OpenAddNew();">Add New</a>

第四步:创建 AddNewLink 行为方法

在 MainController 下创建一个新的行为方法,命名为 GetAddNewLink。

public ActionResult GetAddNewLink(){if (Convert.ToBoolean(Session["IsAdmin"])){return PartialView("AddNewLink");}else{return new EmptyResult();}}

第五步:创建 EmployeeList 视图

在「~/Areas/Spa/Views/Main」文件夹下创建一个新的分部视图,命名为 EmployeeList。


  1. @using WebApplication1.ViewModels.SPA
  2. @model EmployeeListViewModel
  3. <div>
  4.    @{
  5.        Html.RenderAction("GetAddNewLink");
  6.    }
  7.    <table border="1" id="EmployeeTable">
  8.        <tr>
  9. <th>Employee Name</th>

第六步:设置 EmployeeList 为初始页

在「~/Areas/Spa/Views/Main」文件夹下打开 Index.cshtml 文件,在 DivOptions div 中包含 EmployeeList 行为结果。

第七步:执行并测试

按下 F5,并执行应用。

4. Lab 35 — 创建单页应用 — Part 3 — 创建 Employee

第一步:创建 AddNew 视图模型

在 ViewModel 类库项目的 SPA 文件夹下创建一个新的视图模型,命名为 CreateEmployeeViewModel。

namespace WebApplication1.ViewModels.SPA{    public class CreateEmployeeViewModel    {        public string FirstName { get; set; }        public string LastName { get; set; }        public string Salary { get; set; }    }}

第二步:创建 AddNew 行为方法

在 MainController 中引用如下声明。

using WebApplication1.Filters;

在 MainController 下创建 AddNew 行为方法如下。

[AdminFilter]public ActionResult AddNew(){    CreateEmployeeViewModel v = new CreateEmployeeViewModel();    return PartialView("CreateEmployee", v);}

第三步:创建 CreateEmployee 分部视图

在「~/Areas/Spa/Views/Main」文件夹下创建一个新的分部视图,命名为 CreateEmployee。

@using [email protected] CreateEmployeeViewModel<div>    <table>        <tr>            <td>                First Name:</td>

第四步:引入 JQuery UI

右击项目,然后选择「Manage Nuget Manager」。搜索「JQuery UI」。

安装 JQuery UI。它将会向项目中增加几个 JavaScript.js 和 Stylesheet.css 文件。

第五步:包含 JQuery UI

打开「~/Areas/Spa/Views/Main/Index.cshtml」文件,然后包含 JQuery.js,JQueryUI.js 和 All.css 文件。这些文件作为 JQuery UI 的一部分被 Nuget Manager 所添加。

<head><meta name="viewport" content="width=device-width" /><script src="~/Scripts/jquery-1.8.0.js"></script><script src="~/Scripts/jquery-ui-1.11.4.js"></script><title>Employee Single Page Application</title><link href="~/Content/themes/base/all.css" rel="stylesheet" />...

第六步:实现 OpenAddNew 函数

在「~/Areas/Spa/Views/Main/Index.cshtml」下创建一个新的 JavaScript 函数,命名为 OpenAddNew。

<script>    function OpenAddNew() {        $.get("/SPA/Main/AddNew").then            (                function (r) {                    $("<div id=‘DivCreateEmployee‘></div>").html(r).                        dialog({                            width: ‘auto‘, height: ‘auto‘, modal: true, title: "Create New Employee",                            close: function () {                                $(‘#DivCreateEmployee‘).remove();                            }                        });                }            );    }</script>

第七步:执行并测试

按下 F5,执行应用。

完成登录操作,然后导航到 MainController 的 Index 动作。然后点击 Add New 超链接。

第八步:创建 ResetForm 函数

打开 CreateEmployee.cshtml 视图。在顶部创建 ResetForm 函数。

@model CreateEmployeeViewModel<script>    function ResetForm() {        document.getElementById(‘TxtFName‘).value = "";        document.getElementById(‘TxtLName‘).value = "";        document.getElementById(‘TxtSalary‘).value = "";    }</script>

第九步:创建 CancelSave 函数

打开 CreateEmployee.cshtml 视图。在顶部创建 CancelSave 函数。

document.getElementById(‘TxtSalary‘).value = "";    }    function CancelSave() {        $(‘#DivCreateEmployee‘).dialog(‘close‘);}

接下来是什么?

在我们继续第十步之前,我们先要了解接下来应该做什么。

  • 终端用户点击 Save Employee 按钮。
  • 在客户端对控件的值进行合法性验证。
  • 如果所有的值都是合法的,就被传输到服务器端。
  • 在数据库中存储一个新的 Employee 记录。
  • CreateEmployee 对话框关闭。
  • Grid 更新 Employee 记录。

开始计划

  • 认证

对于认证,我们可以运用在 Non-Spa 项目中使用过的认证 JavaScript 代码。

  • 保存 Employee

我们将会创建一个 MVC 行为方法,用于保存 Employee,然后通过 JQuery Ajax 来触发它。

  • 将数据从客户端传输到服务器端

之前通过 Form 标签和 Submit 按钮能轻松并自动的处理这件事。现在我们不能使用该方法,因为它将会带来全部的刷新。取而代之的是,我们利用 JQuery Ajax 的方式,这种方式允许我们触发服务器端的 MVC 行为方法,并且不会全部刷新页面。

现在一个重要的问题是,如果调用是手动的,那么数据是如何通过 JavaScript 传输到 MVC 行为方法中的。

找寻解决方案

理解问题

当你听到数据这个词时,首先映入你脑海的是 JavaScript,.NET,还是其它技术呢?

答案是变量。我们可以利用变量来承载临时数据,然后将其转储在持久性的仓库中,例如数据库。

在我们的例子中,我们用到了两个技术,即 JavaScript 和 ASP.NET MVC。JavaScript 是一个技术,ASP.NET MVC 是另一项技术。

它们不能交换彼此的数据,换句话说,它们不能直接与彼此交换变量。你也许会想知道,为什么它们不能?

它们都有变量。它们都支持数据格式,例如 Float,Int,Char,那么它们为什么不能将变量彼此传输呢?

答案是,它们拥有变量,但是它们不同。.NET 中的整型数据类型没有要求和其它技术的整型数据类型相同。它们也许在大小上不同,或者可能是其它属性。

举一个现实中的有趣例子。每一个人都拥有腿,手,眼睛等。同样,小狗也共同的东西。它们相同吗?显而易见的是,它们不同。人类的眼睛不能被小狗的眼睛所替代,反之亦然。

在所有的技术中,变量的概念是相似的,但是它们却不相同。再一次重复这句话,「.NET 中的整型和 Java 中的整型是有所区别的」。

解决方案 — 一个统一的数据类型

行业已经意识到了这个问题,因此考虑使用在所有技术中相同的数据类型。

String 数据类型正是如此,并且可以承载任何数据。

  • 我们可以将整型数据转换为字符串类型,然后将其存储在字符串变量中。
  • 我们可以将浮点型数据转换为字符串类型,然后将其存储在字符串变量中。
  • 任何数据类型都可以存储在字符串变量中。

终极的解决方案是「每一次从技术1传输数据到技术2,技术1需要将数据转换为字符串类型,然后传输给技术2,因为这样可以100%确保技术2能够理解该字符串」。

目前在行业中已经形成标准。

问题 — 如何处理复杂的数据?

如果这种字符串传输方式成为标准,那么复杂的数据如何传输呢?如果我们想将 Employee 的信息从一个技术传输给另一个呢?

在 .NET 中,「类和对象」被用于呈现复杂数据。看一下如下例子。

Employee e=new Employee();e.EmpName= "Sukesh";e.Address= "Mumbai";

在 JavaScript 中,「JavaScript 对象」用于呈现复杂数据。看一下如下例子。

var e={EmpName= "Sukesh",Address= "Mumbai"};

将复杂的数据从 .NET 传输给其它技术,意味着类对象从 .NET 传输给其它技术,将复杂数据从 JavaScript 传输给其它技术,意味着 JavaScript 对象从 JavaScript 传输给其它技术。

直接传输是不可能的。按照之前的标准,我们先要将 .NET 对象或者 JavaScript 对象转换为字符串类型,然后再发送。

解决方案:一个统一的数据格式标准

就像之前一样,行业也指出一个统一的数据格式标准。说它是统一的,意味着所有人要发送数据之前都需要呈现出他们的数据。因此 XML 应运而生。

所有技术都将数据转换为 XML 格式,然后将其发送给其它技术。就像字符串一样,XML 也被当做一种标准格式,因此每一个技术都知道它。

C# 代码创建的 Employee 对象也能通过 XML 格式这样呈现。

<employee></employee><Employee>      <EmpName>Sukesh</EmpName>      <Address>Mumbai</Address></Employee>

因此解决方案是,「技术1把复杂的数据转换为 XML 格式的字符串数据,然后将其传送给技术2」。

问题 — XML 格式的问题

XML 格式有如下的问题。

  1. XML 格式增加了需要发送的字符串大小。大小越大,意味着转化需要更多的时间,即意味着更差的性能。
  2. 第二个原因,也是最主要的原因:XML 很难创建和解析。

让我们来探讨一下。

  • 正如我们之前所说,每一个技术都需要基于数据创建 XML 字符串,然后传输这个 XML 字符串。现在利用 XML
    Serializers,用 C# 基于 .NET 对象来创建 XML 字符串是容易的。但是对于 JavaScript 而言呢?实际上,
    JavaScript 既没有序列化的概念,也没有 XML 操作库用于使用。因此当 JavaScript 传输数据给其它技术时,我们需要在
    JavaScript 对象中手动创建 XML 字符串。这是一个很艰难的任务。
  • 当一个技术从另一个技术那里接收到数据时,通常都是 XML 格式的字符串。现在通过 XML Deserializers,用 C# 将
    XML 字符串解析并创建 .NET 对象是容易的。但是对于 JavaScript 而言呢?实际上, JavaScript
    既没有反序列化的概念,也没有 XML 操作库用于使用。因此当 JavaScript 解析 XML 字符串时,这是一个很艰难的任务。

解决方案 — JSON

为了解决 XML 格式带来的问题,行业想出一个新的格式,称为 JSON。它是 「JavaScript Object Notation」的缩写。

利用 C# 来创建 Employee 对象,可以通过如下代码来呈现 JSON 格式。

{  EmpName: "Sukesh",  Address: "Mumbai"}

JSON 格式展现的数据和 JavaScript 对象相像,因此这种格式被命名为 JSON(JavaScript Object Notation)。

  • 正如你所看见的,这比之前更轻量级。
  • 有一些完备的函数可用于 JavaScript,即能将 JavaScript 对象转换为 JSON 格式字符串,也能将 JSON 格式字符串解析为 JavaScript 对象。

如下代码展示了如何创建和解析 JSON 字符串。


  1. var e={
  2. EmpName= &ldquo;Sukesh&rdquo;,
  3. Address= &ldquo;Mumbai&rdquo;
  4. };
  5. var EmployeeJsonString = JSON.stringify(e);//This EmployeeJsonString will be send to other technologies.
  6. var EmployeeJsonString=GetFromOtherTechnology();
  7. var e=JSON.parse(EmployeeJsonString);
  8. alert(e.EmpName);
  9. alert(e.Address);

关闭对话框

我们可以运用 JQuery API 来关闭 CreateEmployee 对话框。

更新 Grid

可以通过如下方式来更新 Grid。

  • 通过分部视图

a. 像 CreateEmployee 设计函数一样,创建一个 Grid 的分部视图。

b. 在 EmployeeListView 中创建一个含有 Id 的 Div,然后在里面展示 Grid 的分部视图。

c. 当 Save Employee 按钮被点击时,以分部视图结果的格式更新 Grid,然后用新的 PartialViewResult 来替换内部的 Grid HTML。

我相信迄今为止我们完成的任何一个实验都能给你一个很好的思路来实现这样的目的,因此我们将把其作为一个任务。需要你来亲自完成它。

  • 通过手动代码

在这个方法中,MVC 行为方法将会返回 EmployeeViewModel,而不是
EmloyeeListViewModel,这将会被 JavaScript 接收并运用 JavaScript 来创建一个新的行,然后手动插入到
Grid 中。EmployeeViewModel 将会以 JSON 字符串形式来从 MVC 行为方法中传输到 JavaScript。

回到实验

第十步:创建 SaveEmployee Action

在 MainController 中创建一个新的行为方法,命名为 SaveEmployee。


  1. [AdminFilter]
  2. public ActionResult SaveEmployee(Employee emp)
  3. {
  4.    EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();
  5.    empBal.SaveEmployee(emp);
  6. EmployeeViewModel empViewModel = new EmployeeViewModel();
  7. empViewModel.EmployeeName = emp.FirstName + " " + emp.LastName;
  8. empViewModel.Salary = emp.Salary.Value.ToString("C");
  9. if (emp.Salary > 15000)
  10. {
  11. empViewModel.SalaryColor = "yellow";
  12. }
  13. else
  14. {
  15. empViewModel.SalaryColor = "green";
  16.    }
  17. return Json(empViewModel);
  18. }

现在使用 JSON 的方式来讲字符串从 MVC 行为方法传输到 JavaScript。

第十一步:包含 Validation.js

在上述实验中包含 Validation.js 文件。

@using [email protected] CreateEmployeeViewModel<script src="~/Scripts/Validations.js"></script>

第十二步:创建 SaveEmployee 函数

打开 CreateEmployee.cshtml 视图,在顶部创建 SaveEmployee 函数。


  1. ...
  2. ...
  3.    function SaveEmployee() {
  4.        if (IsValid()) {
  5.            var e =
  6.                {
  7.                    FirstName: $(‘#TxtFName‘).val(),
  8.                    LastName: $(‘#TxtLName‘).val(),
  9.                    Salary: $(‘#TxtSalary‘).val()
  10.                };
  11.            $.post("/SPA/Main/SaveEmployee",e).then(
  12.                function (r) {
  13.                    var newTr = $(‘<tr></tr>‘);
  14.                    var nameTD = $(‘<td></td>‘);
  15.                    var salaryTD = $(‘<td></td>‘);
  16.                    nameTD.text(r.EmployeeName);
  17.                    salaryTD.text(r.Salary);
  18.                    salaryTD.css("background-color", r.SalaryColor);
  19.                    newTr.append(nameTD);
  20.                    newTr.append(salaryTD);
  21.                    $(‘#EmployeeTable‘).append(newTr);
  22.                    $(‘#DivCreateEmployee‘).dialog(‘close‘);
  23.                }
  24.                );
  25.        }
  26.    }
  27. </script>

第十三步:执行并测试

按下 F5,执行应用。

Lab 35 的 Q&A

JSON 方法是用于做什么的?

JSONResult 是 ActionResult 的一个子类。在第六天的学习中,我们谈到了 MVC  请求周期。现在我们再来回顾一下。


ActionResult 类中, ExecuteResult 被声明为抽象的。ActionResult
类的所有子类都以自己的方式来定义它。在第一天的学习中,我们谈论过 ViewResult。在 ViewResult
类中,ExecuteResult 方法将会做如下事情。

  • 它将会创建 ViewPageActivator 类的对象。
  • 它将会选择正确的 ViewEngine,将 ViewPageActivator 对象作为参数传输给 ViewEngine 的构造器。ViewEngine 将会创建 View 类的对象。
  • 它将会触发视图的 RenderView 方法,用于渲染最终 HTML 输出的响应。

当它来自于 JsonResult,ExecuteResult 方法将会:

  • 设置响应内容的类型为「Application/Json」。
  • 运用 JavaScript Serializer,它将会把传输的数据转换为 JSON 格式的字符串。
  • 为响应流书写最终的 JSON 格式字符串。

5. Lab 36 — 创建单页应用 — Part 4 — 批量上传

第一步:创建 SpaBulkUploadController

创建一个新的 AsyncController,称作 SpaBulkUploadController。

namespace WebApplication1.Areas.SPA.Controllers{    public class SpaBulkUploadController : AsyncController    {    }}

第二步:创建 Index 方法

在上述的控制器中创建一个新的行为方法,称为 Index。

[AdminFilter]public ActionResult Index(){    return PartialView("Index");}

第三步:创建 Index 分部视图

在「~/Areas/Spa/Views/SpaBulkUpload」中创建一个新的分部视图,称为 Index。

<div>    Select File : <input type="file" name="fileUpload" id="MyFileUploader" value="" />    <input type="submit" name="name" value="Upload" onclick="Upload();" /></div>

第四步:创建 OpenBulkUpload 方法

在「~/Areas/Spa/Views/Main」文件夹中打开 Index.cshtml,然后创建一个 JavaScript 方法,称为 Index.cshtml。

function OpenBulkUpload() {            $.get("/SPA/SpaBulkUpload/Index").then                (                    function (r) {                        $("<div id=‘DivBulkUpload‘></div>").html(r).dialog({ width: ‘auto‘, height: ‘auto‘, modal: true, title: "Create New Employee",                            close: function () {                                $(‘#DivBulkUpload‘).remove();                            } });                    }                );        }    </script></head><body><div style="text-align:right">

第五步:执行并测试

按下 F5,并执行应用。完成登录操作。导航到 Main 控制器下的 Index 行为,然后点击 BulkUpload 链接。

第六步:创建 FileUploadViewModel

在 ViewModel 类库项目的 SPA 文件夹下创建一个新的视图模型类,称为 FileUploadViewModel。

namespace WebApplication1.ViewModels.SPA{    public class FileUploadViewModel    {        public HttpPostedFileBase fileUpload { get; set; }    }}

第七步:创建 Upload 行为

在 SpaBulkUploadController 下创建一个新的行为方法,称为 Upload。


  1. [AdminFilter]
  2. public async Task<actionresult> Upload(FileUploadViewModel model)
  3. {
  4.    int t1 = Thread.CurrentThread.ManagedThreadId;
  5.    List<employee> employees = await Task.Factory.StartNew<list<employee>>
  6.        (() => GetEmployees(model));
  7.    int t2 = Thread.CurrentThread.ManagedThreadId;
  8.    EmployeeBusinessLayer bal = new EmployeeBusinessLayer();
  9.    bal.UploadEmployees(employees);
  10.    EmployeeListViewModel vm = new EmployeeListViewModel();
  11.    vm.Employees = new List<employeeviewmodel>();
  12.    foreach (Employee item in employees)
  13.    {
  14.        EmployeeViewModel evm = new EmployeeViewModel();
  15.        evm.EmployeeName = item.FirstName + " " + item.LastName;
  16.        evm.Salary = item.Salary.Value.ToString("C");
  17.        if (item.Salary > 15000)
  18.        {
  19.            evm.SalaryColor = "yellow";
  20.        }
  21.        else
  22.        {
  23.            evm.SalaryColor = "green";
  24.        }
  25.        vm.Employees.Add(evm);
  26.    }
  27.    return Json(vm);
  28. }
  29. private List<employee> GetEmployees(FileUploadViewModel model)
  30. {
  31.    List<employee> employees = new List<employee>();
  32.    StreamReader csvreader = new StreamReader(model.fileUpload.InputStream);
  33.    csvreader.ReadLine();// Assuming first line is header
  34.    while (!csvreader.EndOfStream)
  35.    {
  36.        var line = csvreader.ReadLine();
  37.        var values = line.Split(‘,‘);//Values are comma separated
  38.        Employee e = new Employee();
  39.        e.FirstName = values[0];
  40.        e.LastName = values[1];
  41.        e.Salary = int.Parse(values[2]);
  42.        employees.Add(e);
  43.    }
  44.    return employees;
  45. }

正如你所看见的,这次我们返回的是 JsonResult,而不是重定向。

第八步:创建 Upload 函数

在「~/Areas/Spa/Views/SpaBulkUpload」文件夹下打开 Index 视图。然后创建一个 JavaScript 函数,称为 Upload。


  1. <script>
  2.    function Upload() {
  3.        debugger;
  4.        var fd = new FormData();
  5.        var file = $(‘#MyFileUploader‘)[0];
  6.        fd.append("fileUpload", file.files[0]);
  7.        $.ajax({
  8.            url: "/Spa/SpaBulkUpload/Upload",
  9.            type: ‘POST‘,
  10.            contentType: false,
  11.            processData: false,
  12.            data: fd
  13.        }).then(function (e) {
  14.            debugger;
  15.            for (i = 0; i < e.Employees.length; i++)
  16.            {
  17.                var newTr = $(‘<tr></tr>‘);
  18.                var nameTD = $(‘<td></td>‘);
  19.                var salaryTD = $(‘<td></td>‘);
  20.                nameTD.text(e.Employees[i].EmployeeName);
  21.                salaryTD.text(e.Employees[i].Salary);
  22.                salaryTD.css("background-color", e.Employees[i].SalaryColor);
  23.                newTr.append(nameTD);
  24.                newTr.append(salaryTD);
  25.                $(‘#EmployeeTable‘).append(newTr);
  26.            }
  27.            $(‘#DivBulkUpload‘).dialog(‘close‘);
  28.        });
  29.    }
  30. </script>

第九步:执行并测试

创建一个文本文件,如下所示。

按下 F5,并执行应用。

6. 总结

这里,我们完成了 7 天玩转 ASP.NET MVC 的系列学习。我们已经运用 ASP.NET MVC 的功能完成了一个简单的项目。我们也在其中穿插讨论了许多详细的理论概念。

学无止境,虽然 ASP.NET MVC 的系列学习已经告终,但是后续的深入学习,还需要靠脚踏实地,不断地实践和反思。

希望每一个人都享受其中,乐于学习,不断成长。

原文地址:Learn MVC Project in 7 days

OneAPMfor .NET 能够深入到所有 .NET 应用内部完成应用性能管理和监控,包括代码级别性能问题的可见性、性能瓶颈的快速识别与追溯、真实用户体验监控、服务器监控和端到端的应用性能管理。想阅读更多技术文章,请访问 OneAPM 官方博客

时间: 2024-10-14 03:23:25

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

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 — 第 2 天

0. 前言 我相信在开始第 2 天的学习时,你已经顺利地完成了第 1 天的课程. 我们回顾一下第 1 天的主要关注点: 为什么选择 ASP.NET MVC ? ASP.NET Webforms 和 ASP.NET MVC 的对比 理解 ASP.NET MVC 的 Controller 以及 Views 提醒:如果你还没有完成第 1 天的学习,最好先确保完成它.我们的目标是在最后一天用最佳实践和最新技术方法来创建一个小的 MVC 项目.每一天的 Lab 训练中,我们都会比之前一天增加一些实用性的功

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