七天学会ASP.NETMVC(七)——创建单页应用
系列文章
七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC
为平塘等地区用户提供了全套网页设计制作服务,及平塘网站建设行业解决方案。主营业务为成都网站设计、成都网站建设、平塘网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!
七天学会ASP.NET MVC (二)——ASP.NET MVC 数据传递
七天学会ASP.NET MVC (三)——ASP.Net MVC 数据处理
七天学会ASP.NET MVC (四)——用户授权认证问题
七天学会ASP.NET MVC (五)——Layout页面使用和用户角色管理
七天学会ASP.NET MVC (六)——线程问题、异常处理、自定义URL
目录
引言
最后一篇学什么
实验32—整理项目组织结构
关于实验32
实验33——创建单页应用——第一部分—安装
什么是Areas?
关于实验33
实验34——创建单页应用——第二部分—显示Employee
实验35——创建单页应用——第三部分—新建Employee
实验36——创建单页应用——第三部分—上传
实验32 ———整理项目组织结构
实验32与其他实验不同,本实验并不是在之前实验基础之上为程序添加新的功能,实验32主要目的是整理项目结构,使项目条理清晰,能够结构化系统化,便于其他人员理解。
1. 创建解决方案文件夹
右键单击,选择“新解决方案文件夹—>添加—>新解决方案”,命名为“View And Controller”
重复上述步骤 ,创建文件夹“Model”,“ViewModel”,”Data Access Layer”
2. 创建数据访问层工程
右击“Data Access Layer”文件夹,新建类库“DataAccessLayer”。
3. 创建业务层和业务实体项
在Model文件夹下创建新类库“BusinessLayer”和“BusinessEntities”
4. 创建ViewModel 项
在ViewModel 文件夹下新建类库项“ViewModel“
5. 添加引用
为以上创建的项目添加引用,如下:
1. DataAccessLayer 添加 BusinessEntities项
2. BusinessLayer 添加DataAccessLayer和 BusinessEntities项
3. MVC WebApplication 选择 BusinessLayer,BusinessEntities, ViewModel
4. BusinessEntities 添加 System.ComponentModel.DataAnnotations
6. 设置
1.将DataAccessLayer文件夹下的 SalesERPDAL.cs文件,复制粘贴到新创建的 DataAccessLayer 类库中。
2. 删除MVC项目(WebApplication1)的DataAccessLayer文件夹
3. 同上,将Model文件夹中的 Employee.cs, UserDetails.cs 及 UserStatus.cs文件复制到新建的 BusinessEntities文件夹中。
4. 将MVC项目中的Model文件夹的 EmployeeBusinessLayer.cs文件粘贴到新建的 BusinessLayer的文件夹中。
5. 删除MVC中的Model文件夹
6. 将MVC项目的ViewModels文件夹下所有的文件复制到新建的ViewModel 类库项中。
7. 删除ViewModels文件夹
8. 将整个MVC项目剪切到”View And Controller”解决方案文件夹中。
7. Build
选择Build->Build Solution from menu bar,会报错。
8. 改错
1. 给ViewModel项添加System.Web 引用
2. 在DataAccessLayer 和 BusinessLayer中使用Nuget 管理,并安装EF(Entity Framework)(如果对于Nuget的使用有不理解的地方可以查看第三篇博客文章)
注意:在Business Layer中引用EF 是非常必要的,因为Business Layer与DataAccessLayer 直接关联的,而完善的体系架构它自身的业务层是不应该与DataAccessLayer直接关联,因此我们必须使用pattern库,协助完成。
3. 删除MVC 项目中的EF
右击MVC 项目,选择”Manage Nuget packages“选项
在弹出的对话框中选择”Installed Packages“
则会显示所有的已安装项,选择EF,点解卸载。
9. 编译会发现还是会报错
10. 修改错误
报错是由于在项目中既没有引用 SalesERPDAL,也没有引用EF,在项目中直接引用也并不是优质的解决方案。
1. 在DataAccessLayer项中 新建带有静态方法”SetDatabase“的类”DatabaseSettings“
1: using System.Data.Entity;
2: using WebApplication1.DataAccessLayer;
3: namespace DataAccessLayer
4: {
5: public class DatabaseSettings
6: {
7: public static void SetDatabase()
8: {
9: Database.SetInitializer(new DropCreateDatabaseIfModelChanges());
10: }
11: }
12: }
2. 在 BusinessLayer项中新建带有”SetBusiness“ 静态方法的”BusinessSettings“类。
1: using DataAccessLayer;
2:
3: namespace BusinessLayer
4: {
5: public class BusinessSettings
6: {
7: public static void SetBusiness()
8: {
9: DatabaseSettings.SetDatabase();
10: }
11: }
12: }
3. 删除global.asax 中的报错的Using语句 和 Database.SetInitializer 语句。 调用 BusinessSettings.SetBusiness 函数:
1: using BusinessLayer;
2: .
3: .
4: .
5: BundleConfig.RegisterBundles(BundleTable.Bundles);
6: BusinessSettings.SetBusiness();
再次编译程序,会发现成功。
关于实验32
什么是解决方案文件夹?
解决方案文件夹是逻辑性的文件夹,并不是在物理磁盘上实际创建,这里使用解决方案文件夹就是为了使项目更系统化更有结构。
实验33——创建单页应用 1—安装
实验33中,不再使用已创建好的控制器和视图,会创建新的控制器及视图,创建新控制器和视图原因如下:
1. 保证现有的选项完整,也会用于旧版本与新版本对比
2. 学习理解ASP.NET MVC 新概念:Areas
接下来,我们需要从头开始新建controllers, views,ViewModels。
下面的文件可以被重用:
已创建的业务层
已创建的数据访问层
已创建的业务实体
授权和异常过滤器
FooterViewModel
Footer.cshtml
1. 创建新Area
右击项目,选择添加->Area,在弹出对话框中输入SPA,点击确认,生成新的文件夹,因为在该文件夹中不需要Model中Area的文件夹,删掉。
接下来我们先了解一下Areas的概念
Areas
Areas是实现Asp.net MVC 项目模块化管理的一种简单方法。
每个项目由多个模块组成,如支付模块,客户关系模块等。在传统的项目中,采用“文件夹”来实现模块化管理的,你会发现在单个项目中会有多个同级文件夹,每个文件夹代表一个模块,并保存各模块相关的文件。
然而,在Asp.net MVC 项目中使用自定义文件夹实现功能模块化会导致很多问题。
下面是在Asp.Net MVC中使用文件夹来实现模块化功能需要注意的几点:
DataAccessLayer, BusinessLayer, BusinessEntities和ViewModels的使用不会导致其他问题,在任何情况下,可视作简单的类使用。
Controllers—只能保存在Controller 文件夹,但是这不是大问题,从MVC4开始,控制器的路径不再受限。现在可以放在任何文件目录下。
所有的Views必须放在“~/Views/ControllerName” or “~/Views/Shared”文件夹。
2. 创建必要的ViewModels
在ViewModel类库下新建文件夹并命名为SPA,创建ViewModel,命名为”MainViewModel“,如下:
1: using WebApplication1.ViewModels;
2: namespace WebApplication1.ViewModels.SPA
3: {
4: public class MainViewModel
5: {
6: public string UserName { get; set; }
7: public FooterViewModel FooterData { get; set; }//New Property
8: }
9: }
3. 创建Index action 方法
在 MainController 中输入:
1: using WebApplication1.ViewModels.SPA;
2: using OldViewModel=WebApplication1.ViewModels;
在MainController 中新建Action 方法,如下:
1: public ActionResult Index()
2: {
3: MainViewModel v = new MainViewModel();
4: v.UserName = User.Identity.Name;
5: v.FooterData = new OldViewModel.FooterViewModel();
6: v.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value
7: v.FooterData.Year = DateTime.Now.Year.ToString();
8: return View("Index", v);
9: }
using OldViewModel=WebApplication1.ViewModels 这行代码中,给WebApplication1.ViewModels 添加了别名OldViewModel,使用时可直接写成OldViewModel.ClassName这种形式。
如果不定义别名的话,会产生歧义,因为WebApplication1.ViewModels.SPA 和 WebApplication1.ViewModels下有名称相同的类。
4.创建Index View
创建与上述Index方法匹配的View
1: @using WebApplication1.ViewModels.SPA
2: @model MainViewModel
3:
4:
5:
6:
7:
8:Employee Single Page Application
5. 运行测试
关于实验33
为什么在控制器名前需要使用SPA关键字?
在ASP.NET MVC应用中添加area时,Visual Studio会自动创建并命名为“[AreaName]AreaRegistration.cs”的文件,其中包含了AreaRegistration的派生类。该类定义了 AreaName属性和用来定义register路劲信息的 RegisterArea 方法。
在本次实验中你会发现nameSpaArealRegistration.cs文件被存放在“~/Areas/Spa”文件夹下,SpaArealRegistration类的RegisterArea方法的代码如下:
1: context.MapRoute(
2: "SPA_default",
3: "SPA/{controller}/{action}/{id}",
4: new { action = "Index", id = UrlParameter.Optional }
5: );
这就是为什么一提到Controllers,我们会在Controllers前面加SPA关键字。
SPAAreaRegistration的RegisterArea方法是怎样被调用的?
打开global.asax文件,首行代码如下:
1: AreaRegistration.RegisterAllAreas();
RegisterAllAreas方法会找到应用程序域中所有AreaRegistration的派生类,并主动调用RegisterArea方法
是否可以不使用SPA关键字来调用MainController?
AreaRegistration类在不删除其他路径的同时会创建新路径。RouteConfig类中定义了新路径仍然会起作用。如之前所说的,Controller存放的路径是不受限制的,因此它可以工作但可能不会正常的显示,因为无法找到合适的View。
实验34——创建单页应用2—显示Employees
1.创建ViewModel,实现“显示Empoyee”功能
在SPA中新建两个ViewModel 类,命名为”EmployeeViewModel“及”EmployeeListViewModel“:
1: namespace WebApplication1.ViewModels.SPA
2: {
3: public class EmployeeViewModel
4: {
5: public string EmployeeName { get; set; }
6: public string Salary { get; set; }
7: public string SalaryColor { get; set; }
8: }
9: }
1: namespace WebApplication1.ViewModels.SPA
2: {
3: public class EmployeeListViewModel
4: {
5: public ListEmployees { get; set; }
6: }
7: }
8:
注意:这两个ViewModel 都是由非SPA 应用创建的,唯一的区别就在于这次不需要使用BaseViewModel。
2. 创建EmployeeList Index
在MainController 中创建新的Action 方法”EmployeeList“action 方法
1: public ActionResult EmployeeList()
2: {
3: EmployeeListViewModel employeeListViewModel = new EmployeeListViewModel();
4: EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();
5: Listemployees = empBal.GetEmployees();
6:
7: ListempViewModels = new List ();
8:
9: foreach (Employee emp in employees)
10: {
11: EmployeeViewModel empViewModel = new EmployeeViewModel();
12: empViewModel.EmployeeName = emp.FirstName + " " + emp.LastName;
13: empViewModel.Salary = emp.Salary.Value.ToString("C");
14: if (emp.Salary > 15000)
15: {
16: empViewModel.SalaryColor = "yellow";
17: }
18: else
19: {
20: empViewModel.SalaryColor = "green";
21: }
22: empViewModels.Add(empViewModel);
23: }
24: employeeListViewModel.Employees = empViewModels;
25: return View("EmployeeList", employeeListViewModel);
26: }
27:
注意: 不需要使用 HeaderFooterFilter
3. 创建AddNewLink 分部View
之前添加AddNewLink 分部View已经无法使用,因为Anchor标签会造成全局刷新,我们的目标是创建”单页应用“,因此不需要全局刷新。
在”~/Areas/Spa/Views/Main“ 文件夹新建分部View”AddNewLink.cshtml“。
1: Add New
4. 创建 AddNewLink Action 方法
在MainController中创建 ”GetAddNewLink“ action 方法。
1: public ActionResult GetAddNewLink()
2: {
3: if (Convert.ToBoolean(Session["IsAdmin"]))
4: {
5: return PartialView("AddNewLink");
6: }
7: else
8: {
9: return new EmptyResult();
10: }
11: }
5. 新建 EmployeeList View
在“~/Areas/Spa/Views/Main”中创建新分部View 命名为“EmployeeList”。
1: @using WebApplication1.ViewModels.SPA
2: @model EmployeeListViewModel
3:4: @{5: Html.RenderAction("GetAddNewLink");6: }7:8:
9: 10:Employee Name 6. 设置EmployeeList 为初始页面
打开“~/Areas/Spa/Views/Main/Index.cshtml”文件,在Div标签内包含EmployeeList action结果。
1: ...2:7. 运行
实验 35——创建单页应用3—创建Employee
1. 创建AddNew ViewModels
在SPA中新建 ViewModel类库项的ViewModel,命名为“CreateEmployeeViewModel”。
1: namespace WebApplication1.ViewModels.SPA2: {3: public class CreateEmployeeViewModel4: {5: public string FirstName { get; set; }6: public string LastName { get; set; }7: public string Salary { get; set; }8: }9: }2. 创建AddNew action 方法
在MainController中输入using 语句:
1: using WebApplication1.Filters;在MainController 中创建AddNew action 方法:
1: [AdminFilter]2: public ActionResult AddNew()3: {4: CreateEmployeeViewModel v = new CreateEmployeeViewModel();5: return PartialView("CreateEmployee", v);6: }3. 创建 CreateEmployee 分部View
在“~/Areas/Spa/Views/Main”中创建新的分部View“CreateEmployee”
1: @using WebApplication1.ViewModels.SPA2: @model CreateEmployeeViewModel3:4:
5: 6: 7: First Name:8:4. 添加 jQuery UI
右击项目选择“Manage Nuget Manager”。找到“jQuery UI”并安装。
项目中会自动添加.js和.css文件
5. 在项目中添加jQuery UI
打开“~/Areas/Spa/Views/Main/Index.cshtml”,添加jQuery.js,jQueryUI.js 及所有的.css文件的引用。这些文件会通过Nuget Manager添加到jQuery UI 包中。
1:2:3:4:5:Employee Single Page Application 6:7: ...6. 实现 OpenAddNew 方法
在“~/Areas/Spa/Views/Main/Index.cshtml”中新建JavaScript方法“OpenAddNew”。
1:2: function OpenAddNew() {3: $.get("/SPA/Main/AddNew").then4: (5: function (r) {6: $("").html(r).7: dialog({8: width: 'auto', height: 'auto', modal: true, title: "Create New Employee",9: close: function () {10: $('#DivCreateEmployee').remove();11: }12: });13: }14: );15: }16:7. 运行
完成登录步骤后导航到Index中,点击Add New 链接。
8. 创建 ResetForm 方法
在CreateEmployee.cshtml顶部,输入以下代码,创建ResetForm函数:
1: @model CreateEmployeeViewModel2:3: function ResetForm() {4: document.getElementById('TxtFName').value = "";5: document.getElementById('TxtLName').value = "";6: document.getElementById('TxtSalary').value = "";7: }8:9. 创建 CancelSave 方法
在CreateEmployee.cshtml顶部,输入以下代码,创建CancelSave 函数:
1: document.getElementById('TxtSalary').value = "";2: }3: function CancelSave() {4: $('#DivCreateEmployee').dialog('close');5: }在开始下一步骤之前,我们先来了解我们将实现的功能:
最终用户点击保存按钮
输入值必须在客户端完成验证
会将合法值传到服务器端
新Employee记录必须保存到数据库中
CreateEmployee对话框使用完成之后必须关闭
插入新值后,需要更新表格。
为了实现三大功能,先确定一些实现计划:
1.验证
验证功能可以使用之前项目的验证代码。
2.保存功能
我们会创建新的MVC action 方法实现保存Employee,并使用jQuery Ajax调用
3. 服务器端与客户端进行数据通信
在之前的实验中,使用Form标签和提交按钮来辅助完成的,现在由于使用这两种功能会导致全局刷新,因此我们将使用jQuery Ajax方法来替代Form标签和提交按钮。
寻求解决方案
1. 理解问题
大家会疑惑JavaScript和Asp.NET 是两种技术,如何进行数据交互?
解决方案: 通用数据类型
由于这两种技术都支持如int,float等等数据类型,尽管他们的存储方式,大小不同,但是在行业总有一种数据类型能够处理任何数据,称之为最兼容数据类型即字符串类型。
通用的解决方案就是将所有数据转换为字符串类型,因为无论哪种技术都支持且能理解字符串类型的数据。
问题:复杂数据该怎么传递?
.net中的复杂数据通常指的是类和对象,这一类数据,.net与其他技术传递复杂数据就意味着传类对象的数据,从JavaScript给其他技术传的复杂类型数据就是JavaScript对象。因此是不可能直接传递的,因此我们需要将对象类型的数据转换为标准的字符串类型,然后再发送。
解决方案—标准的通用数据格式
可以使用XML定义一种通用的数据格式,因为每种技术都需要将数据转换为XML格式的字符串,来与其他技术通信,跟字符串类型一样,XML是每种技术都会考虑的一种标准格式。
如下,用C#创建的Employee对象,可以用XML 表示为:
1:2:Sukesh 3: Mumbai4:因此可选的解决方案就是,将技术1中的复杂数据转换为XML格式的字符串,然再发送给技术2.
然而使用XML格式可能会导致数据占用的字节数太多,不易发送。数据SiZE越大意味着性能越低效。还有就是XML的创建和解析比较困难。
为了处理XML创建和解析的问题,使用JSON格式,全称“JavaScript Object Notation”。
C#创建的Employee对象用JSON表示:
1: {2: EmpName: "Sukesh",3: Address: "Mumbai"4: }JSON数据是相对轻量级的数据类型,且JAVASCRIPT提供转换和解析JSON格式的功能函数。
1: var e={2: EmpName= “Sukesh”,3: Address= “Mumbai”4: };5: var EmployeeJsonString = JSON.stringify(e);//This EmployeeJsonString will be send to other technologies.1: var EmployeeJsonString=GetFromOtherTechnology();2: var e=JSON.parse(EmployeeJsonString);3: alert(e.EmpName);4: alert(e.Address);数据传输的问题解决了,让我们继续进行实验。
10. 创建 SaveEmployee action
在MainController中创建action,如下:
1: [AdminFilter]2: public ActionResult SaveEmployee(Employee emp)3: {4: EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();5: empBal.SaveEmployee(emp);6:7: EmployeeViewModel empViewModel = new EmployeeViewModel();8: empViewModel.EmployeeName = emp.FirstName + " " + emp.LastName;9: empViewModel.Salary = emp.Salary.Value.ToString("C");10: if (emp.Salary > 15000)11: {12: empViewModel.SalaryColor = "yellow";13: }14: else15: {16: empViewModel.SalaryColor = "green";17: }18: return Json(empViewModel);19: }上述代码中,使用Json方法在MVC action方法到JavaScript之间传Json字符串。
11. 添加 Validation.js 引用
1: @using WebApplication1.ViewModels.SPA2: @model CreateEmployeeViewModel3:12. 创建 SaveEmployee 方法
在CreateEmployee.cshtml View中,创建 SaveEmployee方法:
1: ...2: ...3:4: function SaveEmployee() {5: if (IsValid()) {6: var e =7: {8: FirstName: $('#TxtFName').val(),9: LastName: $('#TxtLName').val(),10: Salary: $('#TxtSalary').val()11: };12: $.post("/SPA/Main/SaveEmployee",e).then(13: function (r) {14: var newTr = $(''); 15: var nameTD = $(''); 16: var salaryTD = $(''); 17:18: nameTD.text(r.EmployeeName);19: salaryTD.text(r.Salary);20:21: salaryTD.css("background-color", r.SalaryColor);22:23: newTr.append(nameTD);24: newTr.append(salaryTD);25:26: $('#EmployeeTable').append(newTr);27: $('#DivCreateEmployee').dialog('close');28: }29: );30: }31: }32:13. 运行
关于实验35
JSON 方法的作用是什么?
返回JSONResult,JSONResult 是ActionResult 的子类。在第六篇博客中讲过MVC的请求周期。
ExecuteResult是ActionResult中声明的抽象方法,ActionResult所有的子类都定义了该方法。在第一篇博客中我们已经讲过ViewResult 的ExecuteResult方法实现的功能,有什么不理解的可以翻看第一篇博客。
实验36——创建单页应用—4—批量上传
1. 创建SpaBulkUploadController
创建新的AsyncController“ SpaBulkUploadController”
1: namespace WebApplication1.Areas.SPA.Controllers2: {3: public class SpaBulkUploadController : AsyncController4: {5: }6: }2. 创建Index Action
在步骤1中的Controller中创建新的Index Action 方法,如下:
1: [AdminFilter]2: public ActionResult Index()3: {4: return PartialView("Index");5: }3. 创建Index 分部View
在“~/Areas/Spa/Views/SpaBulkUpload”中创建 Index分部View
1:2: Select File :3:4:4. 创建 OpenBulkUpload 方法
打开“~/Areas/Spa/Views/Main/Index.cshtml”文件,新建JavaScript 方法OpenBulkUpload
1: function OpenBulkUpload() {2: $.get("/SPA/SpaBulkUpload/Index").then3: (4: function (r) {5: $("").html(r).dialog({ width: 'auto', height: 'auto', modal: true, title: "Create New Employee",6: close: function () {7: $('#DivBulkUpload').remove();8: } });9: }10: );11: }12:13:14:15:5. 运行
6. 新建FileUploadViewModel
在ViewModel SPA文件夹中新建View Model”FileUploadViewModel”。
1: namespace WebApplication1.ViewModels.SPA2: {3: public class FileUploadViewModel4: {5: public HttpPostedFileBase fileUpload { get; set; }6: }7: }7. 创建Upload Action
1: [AdminFilter]2: public async TaskUpload(FileUploadViewModel model) 3: {4: int t1 = Thread.CurrentThread.ManagedThreadId;5: Listemployees = await Task.Factory.StartNew >
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(); 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: else22: {23: evm.SalaryColor = "green";24: }25: vm.Employees.Add(evm);26: }27: return Json(vm);28: }29:30: private ListGetEmployees(FileUploadViewModel model) 31: {32: Listemployees = new List (); 33: StreamReader csvreader = new StreamReader(model.fileUpload.InputStream);34: csvreader.ReadLine();// Assuming first line is header35: while (!csvreader.EndOfStream)36: {37: var line = csvreader.ReadLine();38: var values = line.Split(',');//Values are comma separated39: Employee e = new Employee();40: e.FirstName = values[0];41: e.LastName = values[1];42: e.Salary = int.Parse(values[2]);43: employees.Add(e);44: }45: return employees;46: }47:8. 创建Upload 函数
打开”~/Areas/Spa/Views/SpaBulkUpload”的Index View。创建JavaScript函数,命名为“Upload”
1: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: fd13: }).then(function (e) {14: debugger;15: for (i = 0; i < e.Employees.length; i++)16: {17: var newTr = $(''); 18: var nameTD = $(''); 19: var salaryTD = $(''); 20:21: nameTD.text(e.Employees[i].EmployeeName);22: salaryTD.text(e.Employees[i].Salary);23:24: salaryTD.css("background-color", e.Employees[i].SalaryColor);25:26: newTr.append(nameTD);27: newTr.append(salaryTD);28:29: $('#EmployeeTable').append(newTr);30: }31: $('#DivBulkUpload').dialog('close');32: });33: }34:9. 运行
总结
七天学会MVC 就到这里结束了,谢谢大家的支持,希望大家都能够掌握所讲述的MVC知识,希望都能够进步!
原文链接:http://www.codeproject.com/Articles/1010152/Learn-MVC-Project-in-Days-Day
当前题目:七天学会ASP.NETMVC(七)——创建单页应用
网页路径:http://myzitong.com/article/jghjhs.html