7 天学习 MVC 项目 - 第 7 天






4.81/5 (81投票s)
本文是“7 天学会 MVC 项目”系列第 7 天的后续。
引言
这是愉快的一天。我们终于来到了“7 天学会 MVC 项目”系列的最后阶段。我相信您已经享受了之前的所有日子并从中学习到了一些东西。
请不要忘记在文章末尾留下您的评论/建议/反馈。
议程
- 引言
- 最后一天有什么内容?
- 实验 32 – 组织项目
- 实验 33 – 创建单页应用程序 – 第 1 部分 - 设置
- 实验 34 – 创建单页应用程序 – 第 2 部分 – 显示员工
- 实验 35 – 创建单页应用程序 – 第 3 部分 – 创建员工
- 实验 36 – 创建单页应用程序 – 第 4 部分 – 批量上传
- 结论
- 从 MVC 5 开始
完整系列
我们很高兴地宣布,本文现在已出版为硬拷贝书籍,您可以在 www.amazon.com 和 www.flipkart.com 购买。
实验 32 – 组织项目
这实际上不是与任何新功能相关的实验。这个实验只是为了使项目更加结构化和系统化。
右键单击解决方案,选择“添加”>>“新建解决方案文件夹”。
将文件夹命名为“View And Controller”。
现在重复该步骤,创建三个类似的文件夹,分别命名为“Model”、“ViewModel”和“Data Access Layer”。
右键单击“Data Access Layer”文件夹,创建一个名为“DataAccessLayer”的新类库项目。
在“Model”文件夹中创建两个新的类库项目,分别命名为“BusinessLayer”和“BusinessEntities”。
在“ViewModel”文件夹中创建一个新的类库项目,并将其命名为“ViewModel”。
首先逐个右键单击每个项目。选择“添加”>>“引用”,并按如下选择引用。
-
对于 DataAccessLayer,选择 BusinessEntities
-
对于 BusinessLayer,选择 DataAccessLayer 和 BusinessEntities
-
对于 MVC WebApplication,选择 BusinessLayer、BusinessEntities、ViewModel
-
对于 BusinessEntities,选择 System.ComponentModel.DataAnnotations
-
将 MVC 项目中 DataAccessLayer 文件夹中的 SalesERPDAL.cs 复制到新创建的 DataAccessLayer 类库项目中。
-
从 MVC (WebApplication1) 项目中移除 DataAccessLayer 文件夹。
-
将 MVC 项目中 Model 文件夹中的 Employee.cs、UserDetails.cs 和 UserStatus.cs 复制到新创建的 BusinessEntities 类库项目中。
-
将 MVC 项目中 Model 文件夹中的 EmployeeBusinessLayer.cs 复制到新创建的 BusinessLayer 类库项目中。
-
从 MVC 项目中移除 Model 文件夹。
-
将 MVC 项目中 ViewModels 文件夹中的所有类复制到新创建的 ViewModel 类库项目中。
-
从 MVC 项目中移除 ViewModels 文件夹。
-
将 MVC 项目 (WebApplication1) 移动到“View And Controller”解决方案文件夹中。
从菜单栏中选择“构建”>>“构建解决方案”。
您将收到以下错误消息。
-
将 System.Web 引用添加到 ViewModel 项目。
-
使用 Nuget 管理器在 DataAccessLayer 和 BusinessLayer 项目中安装 Entity Framework。(如果您对如何使用 Nuget 管理器感到困惑,我建议您查看第 3 天)
注意:Business Layer 中需要 Entity Framework 引用,因为 Business Layer 直接连接到 DataAccessLayer。作为一种合适的架构,Business Layer 不应直接连接到 DataAccessLayer。我们可以借助 Repository 模式来实现这一点。Repository 模式将完全超出本系列的范围。
-
从 MVC 项目中移除 EntityFramework。
-
右键单击 MVC 项目并选择“管理 Nuget 包”选项。
-
在“管理 Nuget 包”对话框的左侧部分选择“已安装的包”。
-
右侧部分将显示之前安装的所有包。选择 EntityFramework 并单击“卸载”。
您将收到以下错误。
现在我们在 MVC 项目中既没有 SalesERPDAL 引用,也没有 Entity Framework 引用。添加这些引用不是一个好习惯。最佳实践是控制器不应直接连接到数据访问层。
1. 在 DataAccessLayer 项目中创建一个名为 DatabaseSettings 的新类,其中包含一个名为 SetDatabase 的静态方法,如下所示。
using System.Data.Entity;
using WebApplication1.DataAccessLayer;
namespace DataAccessLayer
{
public class DatabaseSettings
{
public static void SetDatabase()
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<SalesERPDAL>());
}
}
}
2. 在 BusinessLayer 项目中创建一个名为 BusinessSettings 的新类,其中包含一个名为 SetBusiness 的静态方法,如下所示。
using DataAccessLayer;
namespace BusinessLayer
{
public class BusinessSettings
{
public static void SetBusiness()
{
DatabaseSettings.SetDatabase();
}
}
}
3. 从 global.asax 中移除两个错误的 using 语句,并移除 Database.SetInitializer 语句。如下调用 BusinessSettings.SetBusiness 函数。
using BusinessLayer;
.
.
.
BundleConfig.RegisterBundles(BundleTable.Bundles);
BusinessSettings.SetBusiness();
构建应用程序。构建将成功。
确保执行应用程序一次。☻
关于实验 32 的讨论
解决方案文件夹只是逻辑文件夹。它们实际上不会在物理硬盘上创建。它们唯一的目的是使解决方案更加系统化。
实验 33 – 创建单页应用程序 – 第 1 部分 - 设置
现在我们不会对现有控制器和视图进行任何更改。我们将为此实验创建全新的控制器和视图。采取这种步骤的原因是:
-
保持现有选项不变,以便您稍后可以比较早期版本和单页版本并更好地学习。
-
实现并理解 Asp.Net MVC 中的另一个概念,称为区域(Areas)。
如我所说,对于这个实验,我们将从头开始创建新的控制器、视图和 ViewModel。
只重用以下内容:
-
现有业务层
-
现有数据访问层
-
现有业务实体
-
身份验证和异常过滤器
-
FooterViewModel
-
Footer.cshtml
右键单击项目并选择“添加”>>“区域”。将弹出一个对话框。将其设置为 SPA 并选择确定。
它将在我们的项目中创建以下文件夹结构。
显然,与区域相关的 Model 文件夹是不需要的。删除它。
什么是区域(Areas)?
区域只是在 Asp.Net MVC 项目中实现模块的一种方式。
每个项目都由多个模块组成。例如 - 账户模块、客户关系模块、支付网关模块等。
在传统的应用程序开发风格中,我们通常使用“文件夹”来实现这一点。我们在单个项目中创建多个文件夹。每个文件夹代表一个模块。我们将把各自模块的所有相关文件保存在各自的文件夹中。
现在,当涉及到 Asp.Net MVC 时,自定义文件夹将是一个大问题。
假设在 Asp.Net MVC 中,我们将使用简单的文件夹来实现模块。
-
现在 DataAccessLayer、BusinessLayer、BusinessEntities 和 ViewModels 不会产生任何问题。它们是简单的类,因此可以放在任何地方。
-
控制器 - 我们不能把它放在任何地方。它必须放在 Controller 文件夹中。但这不会是一个大问题,因为从 MVC 4 开始,控制器位置的限制已被取消。现在我们可以把它放在我们想要的任何地方。
-
视图 - 不幸的是,对于视图来说,这是不可能的。所有视图都必须放在“~/Views/ControllerName”或“~/Views/Shared”文件夹中。
这就是区域(Areas)的用武之地。
在 ViewModel 类库项目的 SPA 文件夹中创建一个名为 MainViewModel 的新文件夹,并创建一个 ViewModel,如下所示。
using WebApplication1.ViewModels;
namespace WebApplication1.ViewModels.SPA
{
public class MainViewModel
{
public string UserName { get; set; }
public FooterViewModel FooterData { get; set; }//New Property
}
}
在 MainController 中添加以下 using 语句。
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 命名空间添加了一个别名 OldViewModel。现在,我们不需要写 WebApplication1.ViewModels.ClassName,只需写 OldViewModel.ClassName 即可。
不指定别名将导致歧义错误。WebApplication1.ViewModels.SPA 和 WebApplication1.ViewModels 这两个命名空间有一些相似的类。
创建一个与上述索引方法关联的视图,如下所示。
@using WebApplication1.ViewModels.SPA
@model MainViewModel
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Employee Single Page Application</title>
</head>
<body>
<div style="text-align:right">
Hello, @Model.UserName
<a href="/Authentication/Logout">Logout</a>
</div>
<hr />
<div id="DivOption">
</div>
@Html.Partial("Footer", Model.FooterData)
</body>
</html>
按 F5 执行应用程序。完成登录过程并导航到 Main Controller 中的 Index 动作。
关于实验 33 的讨论
当我们向 ASP.NET MVC 应用程序添加一个区域时,Visual Studio 会创建一个名为 [AreaName]AreaRegistration.cs 的文件,其中包含一个派生自 AreaRegistration 的类。该类定义了 AreaName 属性和 RegisterArea 方法,用于注册新区域的路由信息。
在我们的例子中,您会找到一个名为 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 类中定义的路由仍然正常工作。正如我之前所说,控制器位置没有限制。因此它会工作,但输出不会正确显示,因为它无法找到视图。我建议您执行一次应用程序并尝试一下。
实验 34 – 创建单页应用程序 – 第 2 部分 – 显示员工
在 ViewModel 类库的 SPA 文件夹中创建名为 EmployeeViewModel 和 EmployeeListViewModel 的新 ViewModel 类,如下所示。
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 Employees { get; set; }
}
}
注意:这两个 ViewModel 都是为非 Spa 应用程序创建的 ViewModel 的精确复制。唯一的区别是这次不需要 BaseViewModel。
在 MainController 中创建一个名为 EmployeeList 的新动作方法,如下所示。
public ActionResult EmployeeList()
{
EmployeeListViewModel employeeListViewModel = new EmployeeListViewModel();
EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();
List employees = empBal.GetEmployees();
List empViewModels = new List();
foreach (Employee emp in employees)
{
EmployeeViewModel empViewModel = new EmployeeViewModel();
empViewModel.EmployeeName = emp.FirstName + " " + emp.LastName;
empViewModel.Salary = emp.Salary.Value.ToString("C");
if (emp.Salary > 15000)
{
empViewModel.SalaryColor = "yellow";
}
else
{
empViewModel.SalaryColor = "green";
}
empViewModels.Add(empViewModel);
}
employeeListViewModel.Employees = empViewModels;
return View("EmployeeList", employeeListViewModel);
}
注意:不需要 HeaderFooterFilter。
步骤 3 – 创建 AddNewLink PartialView
现在我们不能使用之前的 AddNewLink 局部视图,因为那里的锚标签会导致完全刷新。我们的目标是创建一个“单页应用程序”,因此不应该有任何完全刷新。
在“~/Areas/Spa/Views/Main”文件夹中创建一个名为 AddNewLink.cshtml 的新局部视图。
<a href="#" onclick="OpenAddNew();">Add New</a>
<a href="#" onclick="OpenBulkUpload();">BulkUpload</a>
在 MainController 中创建一个名为 GetAddNewLink 的新动作方法,如下所示。
public ActionResult GetAddNewLink()
{
if (Convert.ToBoolean(Session["IsAdmin"]))
{
return PartialView("AddNewLink");
}
else
{
return new EmptyResult();
}
}
在“~/Areas/Spa/Views/Main”文件夹中创建一个名为 EmployeeList 的新局部视图。
@using WebApplication1.ViewModels.SPA
@model EmployeeListViewModel
<div>
@{
Html.RenderAction("GetAddNewLink");
}
<table border="1" id="EmployeeTable">
<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>
为此,打开位于“~/Areas/Spa/Views/Main”文件夹中的 Index.cshtml,并将 EmployeeList 动作结果包含在 DivOptions div 中,如下所示。
...
</div>
<hr />
<div id="DivOption">
@Html.Action("EmployeeList","Main")
</div>
@Html.Partial("Footer"...
按 F5 并执行应用程序。
实验 35 – 创建单页应用程序 – 第 3 部分 – 创建员工
在 ViewModel 类库项目的 SPA 文件夹中创建一个名为 CreateEmployeeViewModel 的新 ViewModel,如下所示。
namespace WebApplication1.ViewModels.SPA
{
public class CreateEmployeeViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Salary { get; set; }
}
}
在 MainController 中添加以下 using 语句。
using WebApplication1.Filters;
现在在 MainController 中创建 AddNew 动作方法,如下所示。
[AdminFilter]
public ActionResult AddNew()
{
CreateEmployeeViewModel v = new CreateEmployeeViewModel();
return PartialView("CreateEmployee", v);
}
在“~/Areas/Spa/Views/Main”文件夹中创建一个名为 CreateEmployee 的新局部视图,如下所示。
@using WebApplication1.ViewModels.SPA
@model CreateEmployeeViewModel
<div>
<table>
<tr>
<td>
First Name:
</td>
<td>
<input type="text" id="TxtFName" name="FirstName" value="@Model.FirstName" />
</td>
</tr>
<tr>
<td colspan="2" align="right">
@Html.ValidationMessage("FirstName")
</td>
</tr>
<tr>
<td>
Last Name:
</td>
<td>
<input type="text" id="TxtLName" name="LastName" value="@Model.LastName" />
</td>
</tr>
<tr>
<td colspan="2" align="right">
@Html.ValidationMessage("LastName")
</td>
</tr>
<tr>
<td>
Salary:
</td>
<td>
<input type="text" id="TxtSalary" name="Salary" value="@Model.Salary" />
</td>
</tr>
<tr>
<td colspan="2" align="right">
@Html.ValidationMessage("Salary")
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" name="BtnSubmit" value="Save Employee" onclick="SaveEmployee();" />
<input type="submit" name="BtnSubmit" value="Cancel" onclick="CancelSave()” />
<input type="button" name="BtnReset" value="Reset" onclick="ResetForm();" />
</td>
</tr>
</table>
</div>
右键单击项目并选择“管理 Nuget 管理器”。
搜索“jQuery UI”。
安装 jQuery UI。
它将向项目添加几个 JavaScript (.js) 和样式表 (.css) 文件。
打开“~/Areas/Spa/Views/Main/Index.cshtml”,并包含 jQuery.js、jQueryUI.js 和 all.css,如下所示。所有这些文件都是通过 Nuget 管理器作为 jQuery UI 包的一部分添加的。
<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" />
...
在“~/Areas/Spa/Views/Main/Index.cshtml”中创建一个名为 OpenAddNew 的新 JavaScript 函数,如下所示。
<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 动作。最后点击“添加新”超链接。
打开 CreateEmployee.cshtml 视图。在顶部创建 ResetForm 函数,如下所示。
@model CreateEmployeeViewModel
<script>
function ResetForm() {
document.getElementById('TxtFName').value = "";
document.getElementById('TxtLName').value = "";
document.getElementById('TxtSalary').value = "";
}
</script>
<div>
<table>
......
......
<input type="submit" name="BtnSubmit" value="Save Employee" onclick="SaveEmployee();" />
<input type="submit" name="BtnSubmit" value="Cancel" onclick="CancelSave()” />
<input type="button" name="BtnReset" value="Reset" onclick="ResetForm();" />
</td>
</tr>
......
……
打开 CreateEmployee.cshtml 视图。在顶部创建 CancelSave 函数,如下所示。
document.getElementById('TxtSalary').value = "";
}
function CancelSave() {
$('#DivCreateEmployee').dialog('close');
}
接下来是什么?
在我们继续执行步骤 10 之前,首先让我们了解接下来要做什么。
-
最终用户点击“保存员工”按钮。
-
控件中的值必须在客户端进行验证。
-
如果所有值都有效,所有值都将传输到服务器端。
-
新的员工记录必须保存到数据库中。
-
之后 CreateEmployee 对话框必须关闭。
-
网格将使用新值进行更新。
让我们计划一下
验证
对于验证,我们可以使用与非 SPA 版本项目中使用的相同的验证 JavaScript 代码。
保存员工
我们将创建一个 MVC 动作方法来保存员工,并使用 jQuery Ajax 调用它。
数据从客户端传输到服务器端
之前,这可以通过 Form 标签和 Submit 按钮轻松自动处理。现在我们不能遵循这种方法,因为它会导致完全刷新。取而代之的是,这次我们使用 jQuery Ajax 方式,它允许我们在不刷新页面的情况下调用服务器端 MVC 动作方法。
现在,百万美元的问题是,如果调用是手动的,数据将如何从 JavaScript 传输到 MVC 动作方法。
寻找解决方案的旅程
无论是 JavaScript、.net 还是任何其他技术,当您第一次听到“数据”这个词时,您会想到什么?
是变量。我们使用变量临时保存数据,然后将其转储到数据库等持久存储中。
在我们的案例中,我们正在使用两种技术——JavaScript 和 Asp.Net MVC。
JavaScript 是一种技术,Asp.Net MVC 是另一种。
它们不能直接交换数据,或者简单地说,它们不能直接交换变量。
您可能想知道,为什么它们不能?
它们都有变量。这两种技术都支持浮点型、整型和字符型等数据类型,那为什么它们不能直接相互发送变量呢?
答案是,它们有,但它们不相同。
.Net 中的整型数据类型不一定与其他技术中的整型数据类型相同。它们的大小可能不同,或者其他属性可能不同。
举一个真实的例子。
人有腿、手、眼睛等。
狗也有共同之处。
它们都一样吗?当然不。人的眼睛不能被狗的眼睛代替,反之亦然。
同样,我们在所有技术中都有变量的概念,但它们并不相同。再重复一遍:“.Net 中的整数与 Java 中的整数不同”。
业界在过去已经意识到这个问题,并思考了一种在所有地方都相同的通用数据类型。一种能够容纳任何数据的数据类型,我们称之为“最兼容的数据类型”,那就是字符串数据类型。
字符串数据类型无处不在,它可以容纳任何内容。
-
我们可以将整数数据转换为字符串类型并存储在字符串变量中。
-
我们可以将浮点数数据转换为字符串类型并存储在字符串变量中。
-
…
-
任何数据都可以存储在字符串类型的变量中。
最终的解决方案是“每当需要将数据从技术 1 发送到技术 2 时,技术 1 会将数据转换为字符串类型并发送给技术 2,因为技术 2 百分之百会理解字符串”。
现在这已成为行业的标准。
问题 – 复杂数据怎么办?
如果是,复杂数据怎么办?
如果我们想将员工信息从一种技术传输到另一种技术怎么办?
在 .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>
<EmpName>Sukesh</EmpName>
<Address>Mumbai</Address>
</Employee>
因此,解决方案是,“技术 1 将其复杂数据转换为 XML 格式的字符串数据,然后将该字符串发送给技术 2”。
XML 格式存在以下问题:
1. XML 格式增加了需要发送的整个字符串的大小。
字符串大小越大意味着传输所需的时间越长。这意味着性能越差。
2. 第二个原因,也是主要原因是,XML 难以创建和解析。
让我们更深入地讨论一下。
-
正如我之前所说,每种技术都必须根据数据创建 XML 字符串,然后发送该 XML 字符串。现在在 C# 中,借助 XML 序列化器,从 .Net 对象创建 XML 字符串很容易。
但是 JavaScript 呢?在 JavaScript 中,我们既没有序列化器的概念,也没有现成的 XML 操作库。因此,当需要从 JavaScript 向其他技术发送数据时,我们必须手动从 JavaScript 对象创建 XML 字符串。这是一项非常困难的任务。 -
当一种技术从另一种技术接收数据时,它总是 XML 格式的字符串。现在在 C# 中,借助 XML 反序列化器,解析 XML 字符串并从中创建 .Net 对象将非常容易。
但是 JavaScript 呢?在 JavaScript 中,我们既没有 XML 反序列化器,也没有 XML 操作库,因此解析 XML 字符串在这里是一项非常困难的任务。
为了解决 XML 格式的问题,业界提出了一种新的格式,称为 JSON。它是“JavaScript Object Notation”的缩写。
上面 C# 代码中创建的 Employee 对象可以用 JSON 格式表示如下。
{
EmpName: "Sukesh",
Address: "Mumbai"
}
以 JSON 格式表示的数据看起来像 JavaScript 对象,因此这种格式被命名为 JSON (JavaScript Object Notation)
-
如您所见,与之前相比,它更轻量。
-
JavaScript 中提供了现成的函数,用于将 JavaScript 对象转换为 JSON 格式的字符串,以及将 JSON 格式的字符串解析为 JavaScript 对象。
以下代码解释了 JSON 字符串的创建和解析。
var e={
EmpName= “Sukesh”,
Address= “Mumbai”
};
var EmployeeJsonString = JSON.stringify(e);//This EmployeeJsonString will be send to other technologies.
var EmployeeJsonString=GetFromOtherTechnology();
var e=JSON.parse(EmployeeJsonString);
alert(e.EmpName);
alert(e.Address);
关闭对话框
要关闭 CreateEmployee 对话框,我们可以使用 jQuery API。
更新网格
网格更新可以通过以下两种方式之一完成。
1. 使用局部视图
-
为网格创建一个局部视图,就像我们为 CreateEmployee 功能所做的那样。
-
在 EmployeeList 视图中创建一个带有一些 ID 的 div,并在其中显示局部视图(网格)。
-
当点击“保存员工”按钮时,以局部视图结果的形式获取更新后的网格,并用新的 PartialViewResult 替换网格 div 的内部 HTML。
我相信我们目前完成的所有实验都会给您一个很好的思路,说明如何实现这一点,因此我将其作为一项作业。完成它,别忘了留下关于经验的评论。
2. 使用手动代码
在这种方法中,MVC 动作方法将返回单个 EmployeeViewModel 而不是 EmployeeListViewModel,该 ViewModel 将在 JavaScript 端接收,并且使用 JavaScript,新行将手动插入到网格中。(EmployeeViewModel 将作为 JSON 字符串从 MVC 动作方法发送到 JavaScript)。
回到我们的实验
现在在 MainController 中创建一个名为 SaveEmployee 的新动作,如下所示。
[AdminFilter]
public ActionResult SaveEmployee(Employee emp)
{
EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();
empBal.SaveEmployee(emp);
EmployeeViewModel empViewModel = new EmployeeViewModel();
empViewModel.EmployeeName = emp.FirstName + " " + emp.LastName;
empViewModel.Salary = emp.Salary.Value.ToString("C");
if (emp.Salary > 15000)
{
empViewModel.SalaryColor = "yellow";
}
else
{
empViewModel.SalaryColor = "green";
}
return Json(empViewModel);
}
目前,只需将 Json 方法作为从 MVC 动作方法向 JavaScript 发送 Json 字符串的一种方式。
按如下方式包含在先前实验中创建的 Validation.js 文件。
@using WebApplication1.ViewModels.SPA
@model CreateEmployeeViewModel
<script src="~/Scripts/Validations.js"></script>
<script>
function ResetForm() {
......
打开 CreateEmployee.cshtml 视图。在顶部创建 SaveEmployee 函数,如下所示。
...
...
function SaveEmployee() {
if (IsValid()) {
var e =
{
FirstName: $('#TxtFName').val(),
LastName: $('#TxtLName').val(),
Salary: $('#TxtSalary').val()
};
$.post("/SPA/Main/SaveEmployee",e).then(
function (r) {
var newTr = $('<tr></tr>');
var nameTD = $('<td></td>');
var salaryTD = $('<td></td>');
nameTD.text(r.EmployeeName);
salaryTD.text(r.Salary);
salaryTD.css("background-color", r.SalaryColor);
newTr.append(nameTD);
newTr.append(salaryTD);
$('#EmployeeTable').append(newTr);
$('#DivCreateEmployee').dialog('close');
}
);
}
}
</script>
按 F5 并执行应用程序。
关于实验 35 的讨论
它将返回 JSONResult。
JSONResult 是 ActionResult 的子类之一。
在第 6 天,我们讨论了 MVC 请求周期。让我们再回顾一下。
ExecuteResult 在 ActionResult 类中被声明为抽象方法。ActionResult 类的所有子类都以自己的方式定义它。在第 1 天,我们讨论了 ViewResult。在 ViewResult 类中,ExecuteResult 方法将执行以下操作。
-
它将创建 ViewPageActivator 类的对象。
-
它将选择正确的 ViewEngine 并将 ViewPageActivator 对象作为参数传递给 ViewEngine 的构造函数。ViewEngine 创建 View 类的对象。
-
它将调用 View 的 RenderView 方法,该方法将渲染最终的 HTML 输出到响应中。
当涉及到 JsonResult 时,ExecuteResult 方法将:
-
将响应内容类型设置为“Application/Json”。
-
使用 JavaScript 序列化器将其传递的数据转换为 JSON 格式的字符串。
-
将最终的 JSON 格式字符串写入响应流。
实验 36 – 创建单页应用程序 – 第 4 部分 – 批量上传
步骤 1 – 创建 SpaBulkUploadController
创建一个名为 SpaBulkUploadController 的新 AsyncController。
namespace WebApplication1.Areas.SPA.Controllers
{
public class SpaBulkUploadController : AsyncController
{
}
}
步骤 2 – 创建 Index 动作
在上述控制器中创建一个名为 Index 的新动作方法,如下所示。
[AdminFilter]
public ActionResult Index()
{
return PartialView("Index");
}
步骤 3 – 创建 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>
步骤 4 – 创建 OpenBulkUpload 方法
打开“~/Areas/Spa/Views/Main”文件夹中的 Index.cshtml,并创建一个名为 Index.cshtml 的 JavaScript 方法,如下所示。
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 执行应用程序。完成登录过程。
导航到主控制器的 Index 动作并点击 BulkUpload 链接。
步骤 6 – 创建 FileUploadViewModel
在 ViewModel 类库的 SPA 文件夹中创建一个名为 FileUploadViewModel 的新视图模型类。
namespace WebApplication1.ViewModels.SPA
{
public class FileUploadViewModel
{
public HttpPostedFileBase fileUpload { get; set; }
}
}
步骤 7 – 创建 Upload 动作
在 SpaBulkUploadController 中创建一个名为 Upload 的新动作方法,如下所示。
[AdminFilter]
public async Task Upload(FileUploadViewModel model)
{
int t1 = Thread.CurrentThread.ManagedThreadId;
List employees = await Task.Factory.StartNew>
(() => GetEmployees(model));
int t2 = Thread.CurrentThread.ManagedThreadId;
EmployeeBusinessLayer bal = new EmployeeBusinessLayer();
bal.UploadEmployees(employees);
EmployeeListViewModel vm = new EmployeeListViewModel();
vm.Employees = new List();
foreach (Employee item in employees)
{
EmployeeViewModel evm = new EmployeeViewModel();
evm.EmployeeName = item.FirstName + " " + item.LastName;
evm.Salary = item.Salary.Value.ToString("C");
if (item.Salary > 15000)
{
evm.SalaryColor = "yellow";
}
else
{
evm.SalaryColor = "green";
}
vm.Employees.Add(evm);
}
return Json(vm);
}
private List GetEmployees(FileUploadViewModel model)
{
List employees = new List();
StreamReader csvreader = new StreamReader(model.fileUpload.InputStream);
csvreader.ReadLine();// Assuming first line is header
while (!csvreader.EndOfStream)
{
var line = csvreader.ReadLine();
var values = line.Split(',');//Values are comma separated
Employee e = new Employee();
e.FirstName = values[0];
e.LastName = values[1];
e.Salary = int.Parse(values[2]);
employees.Add(e);
}
return employees;
}
如您所见,这次我们返回的是 JsonResult,而不是重定向。
步骤 8 – 创建 Upload 函数
打开“~/Areas/Spa/Views/SpaBulkUpload”文件夹中的 Index 视图。
并创建一个名为 Upload 的 JavaScript 函数,如下所示。
<script>
function Upload() {
debugger;
var fd = new FormData();
var file = $('#MyFileUploader')[0];
fd.append("fileUpload", file.files[0]);
$.ajax({
url: "/Spa/SpaBulkUpload/Upload",
type: 'POST',
contentType: false,
processData: false,
data: fd
}).then(function (e) {
debugger;
for (i = 0; i < e.Employees.length; i++)
{
var newTr = $('<tr></tr>');
var nameTD = $('<td></td>');
var salaryTD = $('<td></td>');
nameTD.text(e.Employees[i].EmployeeName);
salaryTD.text(e.Employees[i].Salary);
salaryTD.css("background-color", e.Employees[i].SalaryColor);
newTr.append(nameTD);
newTr.append(salaryTD);
$('#EmployeeTable').append(newTr);
}
$('#DivBulkUpload').dialog('close');
});
}
</script>
<div>
Select File : ......
步骤 9 – 执行并测试
创建一个文本文件进行测试,如下所示。
按 F5 并执行应用程序。
结论
至此,我们完成了 7 天的旅程。我们使用 Asp.Net MVC 的许多功能完成了一个简单的项目。我们还详细讨论了许多理论概念。
我很快会发布另外两篇关于 MVC 的文章。它们不属于本系列的一部分,但包含了一些在这 7 天的项目中未涵盖的内容。
- 文章 2 – 7 天学会 MVC 项目 - 额外第 1 天
在这里,我们将再次创建一个 SPA 项目,但借助 Web API 和 Angular
- 文章 1 - 7 天学会 MVC 项目 – 额外第 1 天
在这里,我们将讨论一些在 7 天内未涵盖的 MVC 主题。-
捆绑和小型化
-
Application_Error
-
TempData
-
创建自定义辅助类
-
MVC 单元测试
-
MVC 依赖解析器
-
从 MVC 5 开始
如果您想从 MVC 5 开始,请从下面的视频“2 天学会 MVC 5”开始。

如果您认为还需要包含更多内容,请随时给我发送电子邮件。
您的评论、邮件总是激励我们做得更多。请在下方发表您的想法和评论或发送邮件至 SukeshMarla@Gmail.com
在 Facebook、LinkedIn 或 Twitter 上关注我们,以获取最新发布信息。
如需在孟买进行线下技术培训,请访问 StepByStepSchools.Net
如需在线培训,请访问 JustCompile.com 或 www.Sukesh-Marla.com