七天学会ASP.NET MVC (三)——ASP.Net MVC 数据处理

    xiaoxiao2023-03-25  6

    第三天我们将学习Asp.Net中数据处理功能,了解数据访问层,EF,以及EF中常用的代码实现方式,创建数据访问层和数据入口,处理Post数据,以及数据验证等功能。

    系列文章

    七天学会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

    七天学会ASP.NET MVC(七)——创建单页应用

    目录:

    数据访问层

    实体框架(EF)简述

    什么是代码优先的方法?

    实验8——在项目中添加数据访问层

    关于实验8

    实验9——创建数据输入屏幕

    实验10——获取服务端或控制器端传递的数据。

    实验11——重置及取消按钮

    实验12——保存数据。库记录并更新表格

    实验13——添加服务器端验证

    实验14——自定义服务器端验证

    结论

    数据访问层

    在实际开发中,如果一个项目不包含任何数据库,那么这个项目是不完整的,我们在一二节实例中未涉及数据库,在本节开始,实验8中讲解一个关于数据库和数据库层的实例。

    本节将使用SQL Server和EF(Entity Framework)创建相关的数据库及数据库访问层。

    简述实体框架(EF)

    EF是一种ORM工具,ORM表示对象关联映射。

    在RDMS中,对象称为表格和列对象,而在.net中(面向对象)称为类,对象以及属性。

    任何数据驱动的应用实现的方式有两种:

    1. 通过代码与数据库关联(称为数据访问层或数据逻辑层)

    2. 通过编写代码将数据库数据映射到面向对象数据,或反向操作。

    ORM是一种能够自动完成这两种方式的工具。EF是微软的ORM工具。

    什么是代码优先的方法?

    EF提供了三种方式来实现项目:

    数据库优先方法——创建数据库,包含表,列以及表之间的关系等,EF会根据数据库生成相应的Model类(业务实体)及数据访问层代码。

    模型优先方法——模型优先指模型类及模型之间的关系是由Model设计人员在VS中手动生成和设计的,EF将模型生成数据访问层和数据库。

    代码优先方法——代码优先指手动创建POCO类。这些类之间的关系使用代码定义。当应用程序首次执行时,EF将在数据库服务器中自动生成数据访问层以及相应的数据库。

    什么是POCO类?

    POCO即Plain Old CLR对象,POCO类就是已经创建的简单.Net类。在上两节的实例中,Employee类就是一个简单的POCO类。

    实验8——添加数据访问层

    1. 创建数据库

    连接SQL SERVER ,创建数据库 “SalesERPDB”。

    2. 创建连接字符串(ConnectionString

    打开Web.Config 文件,在< Configuration >标签内添加以下代码:

    1: <connectionStrings> 2: <add connectionString="Data Source=(local);Initial Catalog=SalesERPDB;Integrated Security=True" 3: name="SalesERPDAL" 4: providerName="System.Data.SqlClient"/> 5: </connectionStrings>

    3. 添加EF引用

    右击项目->管理Nuget 包。选择Entity Framework 并点击安装。

    4. 创建数据访问层

    在根目录下,新建文件夹”Data Access Layer“,并在Data Access Layer文件夹中新建类” SalesERPDAL “在类文件顶部添加 Using System.Data.Entity代码。继承DbContext类 1: public class SalesERPDAL: DbContext 2: { 3: }

    5. 创建Employee类的主键

    打开Employee类,输入using语句

    1: using System.ComponentModel.DataAnnotations;

    添加Employee的属性,并使用Key 关键字标识主键。

    1 2 3 4 5 6 7 8 public  class  Employee {      [Key]      public  int  EmployeeId  {  get ;  set ; }      public  string  FirstName {  get ;  set ; }      public  string  LastName {  get ;  set ; }      public  int  Salary {  get ;  set ; } }

    6. 定义映射关系

    在SalesERPDAL类文件输入using语句。

    1: using WebApplication1.Models;

    在 SalesERPDAL 类中重写 OnModelCreating方法,代码如下:

    1: protected override void OnModelCreating(DbModelBuilder modelBuilder) 2: { 3: modelBuilder.Entity<employee>().ToTable("TblEmployee"); 4: base.OnModelCreating(modelBuilder); 5: } 6: </employee>

    注意:上述代码中提到“TblEmployee”是表名称,是运行时自动生成的。

    7. 在数据库中添加新属性Employee

    在 SalesERPDAL 类中添加新属性 Employee。

    1: public DbSet<employee> Employees{get;set;} 2: </employee>  

    DbSet表示数据库中能够被查询的所有Employee

    8. 改变业务层代码,并从数据库中获取数据

    打开 EmployeeBusinessLayer 类,输入Using 语句。

    1: using WebApplication1.DataAccessLayer;

    修改GetEmployees 方法:

    1: public List<employee> GetEmployees() 2: { 3: SalesERPDAL salesDal = new SalesERPDAL(); 4: return salesDal.Employees.ToList(); 5: } 6: </employee>

    9. 运行并测试

    id="iframe_0.9663849210214868" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.codeproject.com/KB/aspnet/986730/G.3.png?_=0.6876521897044607%22%20style=%22border:none;max-width:711px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.9663849210214868',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 361px; height: 243px;">

    右击,查看并没有任何Employee的表格,查看数据库文件,我们会看到 TblEmployee 表

    id="iframe_0.9440163412799234" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.codeproject.com/KB/aspnet/986730/H.4.png?_=0.35016947296950107%22%20style=%22border:none;max-width:711px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.9440163412799234',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 295px; height: 331px;">

    10. 插入测试数据

    在TblEmployee 中插入一些测试数据

    id="iframe_0.4320255680420393" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.codeproject.com/KB/aspnet/986730/I.5.png?_=0.7751507898381895%22%20style=%22border:none;max-width:711px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.4320255680420393',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 440px; height: 140px;">

    11. 运行程序

    id="iframe_0.23464987774913837" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.codeproject.com/KB/aspnet/986730/J.6.png?_=0.5268607589340457%22%20style=%22border:none;max-width:711px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.23464987774913837',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 423px; height: 356px;">

     

    关于实验8

    什么是数据集?

    DbSet数据集是数据库方面的概念 ,指数据库中可以查询的实体的集合。当执行Linq 查询时,Dbset对象能够将查询内部转换,并触发数据库。

    在本实例中,数据集是Employees,是所有Employee的实体的集合。当每次需要访问Employees时,会获取“TblEmployee”的所有记录,并转换为Employee对象,返回Employee对象集。

    如何连接数据访问层和数据库?

    数据访问层和数据库之间的映射通过名称实现的,在实验8中,ConnectionString(连接字符串)的名称和数据访问层的类名称是相同的,都是SalesERPDAL,因此会自动实现映射。

    连接字符串的名称可以改变吗?

    可以改变,在实验8中,在数据访问层中定义了构造函数,如下:

    1: public SalesERPDAL():base("NewName") 2: { 3: }

    实验8已经完成,相信大家已经了解基本的原理和操作,接下来我们将做一些改变,使得每件事情都变得有组织有意义:

    1. 重命名

    将“TestController”改成 “EmployeeController”GetView的行为方法改为IndexViews文件夹目录下的Test 文件夹改成EmployeeMyView的View 改为“Index”

    2. 删除EmployeeListViewModel 的 UserName  属性

    3. 删除View中的 UserName

    打开 Views/Employee.Index.cshtml View ,删除 UserName ,即删除以下代码:

    1: Hello @Model.UserName 2: <hr />

    4. 修改 EmployeeController 中的 Index方法

    根据以下代码修改Index 方法,执行时URL会变成  “…./Employee/Index”

    public ActionResult Index() { &hellip;&hellip; &hellip;&hellip; &hellip;&hellip; employeeListViewModel.Employees = empViewModels; //employeeListViewModel.UserName = "Admin";-->Remove this line -->Change1 return View("Index", employeeListViewModel);//-->Change View Name -->Change 2 }

     

    实验 9——创建数据入口(Data Entry Screen)

    1. 新建 action  方法

    在 EmployeeController 中新建 “AddNew”action 方法:

    1: public ActionResult AddNew() 2: { 3: return View("CreateEmployee"); 4: }  

    2. 创建 View

    在View/Employee目录下 新建 View 命名为:CreateEmployee。

    1: @{ 2: Layout = null; 3: } 4: <!DOCTYPE html> 5: <html> 6: <head> 7: <meta name="viewport" content="width=device-width" /> 8: <title>CreateEmployee</title> 9: </head> 10: <body> 11: <div> 12: <form action="/Employee/SaveEmployee" method="post"> 13: First Name: <input type="text" id="TxtFName" name="FirstName" value="" /><br /> 14: Last Name: <input type="text" id="TxtLName" name="LastName" value="" /><br /> 15: Salary: <input type="text" id="TxtSalary" name="Salary" value="" /><br /> 16: <input type="submit" name="BtnSave" value="Save Employee" /> 17: <input type="button" name="BtnReset" value="Reset" /> 18: </form> 19: </div> 20: </body> 21: </html>

    3. 创建Index View的链接

    打开 Index.cshtml 文件,添加指向 AddNew action方法的链接

    1: <ahref="/Employee/AddNew">Add New</a>

    4. 运行

     

    id="iframe_0.5262352571837086" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.codeproject.com/KB/aspnet/986730/K.7.png?_=0.6791129599944952%22%20style=%22border:none;max-width:711px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.5262352571837086',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 711px; height: 294px;">

    关于实验9

    使用Form 标签的作用是什么?

    在系列文章第一讲中,我们已经知道,Web编程模式不是事件驱动的编程模式,是请求响应模式。最终用户会产生发送请求。Form标签是HTML中产生请求的一种方式,Form标签内部的提交按钮只要一被点击,请求会被发送到相关的action 属性。

    Form标签中方法属性是什么?

    方法属性决定了请求类型。有四种请求类型:get,post,put以及delete.

    Get: 当需要获取数据时使用。Post: 当需要新建一些事物时使用。Put: 当需要更新数据时使用。Delete:需要删除数据时使用。

    使用Form 标签来生成请求,与通过浏览器地址栏或超链接来生成请求,有什么区别?

    使用Form标签生成请求时,所有有关输入的控件值会随着请求一起发送。

    输入的值是怎样发送到服务器端的?

    当请求类型是Get,Put或Delete时,值会通过查询语句发送,当请求是Post类型,值会通过Post数据传送。

    使用输入控件名的作用是什么?

    所有输入控件的值将随着请求一起发送。同一时间可能会接收到多个值,为了区分发送到所有值为每个值附加一个Key,这个Key在这里就是名称属性。

    名称和 Id的作用是否相同?

    不相同,名称属性是HTML内部使用的,当请求被发送时,然而 ID属性是在JavaScript中开发人员为了实现一些动态功能而调用的。

    “input type=submit” 和 “input type=button”的区别是什么?

    提交按钮在给服务器发送请求而专门使用的,而简单的按钮是执行一些自定义的客户端行为而使用的。按钮不会自己做任何事情。

    实验10——在服务器端(或Controller)获取Post数据

    1. 创建 SaveEmployee  action 方法

    在 Employee控制器中创建 名为 ”SaveEmployee“ action 方法:

    1: public string SaveEmployee(Employee e) 2: { 3: return e.FirstName + "|"+ e.LastName+"|"+e.Salary; 4: }

    2. 运行

    id="iframe_0.31318386850108393" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.codeproject.com/KB/aspnet/986730/L.8.png?_=0.10317029491880891%22%20style=%22border:none;max-width:711px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.31318386850108393',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 429px; height: 237px;">

    关于实验10

    id="iframe_0.3743437236371805" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.codeproject.com/KB/aspnet/986730/L.8.png?_=0.8657637253912116%22%20style=%22border:none;max-width:711px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.3743437236371805',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 429px; height: 237px;">

    action 方法内部的Textbox 值是如何更新 Employee 对象的?

    在 Asp.Net MVC中有个 Model Binder的概念:

    无论请求是否由带参的action方法生成,Model Binder都会自动执行。Model Binder会通过方法的元参数迭代,然后会和接收到参数名称做对比。如果匹配,则响应接收的数据,并分配给参数。在Model Binder迭代完成之后,将类参数的每个属性名称与接收的数据做对比,如果匹配,则响应接收的数据,并分配给参数。

    如果两个参数是相关联的会发生什么状况,如参数”Employee e“和 “string FirstName”?

    FirstName会被元 First Name变量和 e.FirstName 属性更新。

    Model Binder是组合的关系吗?

    是,在实验 9 中都是根据控件名称执行的。

    例如:

    Customer 类和 Address 类:

    1: public class Customer 2: { 3: public string FName{get;set;} 4: public Address address{get;set;} 5: } 6: public class Address 7: { 8: public string CityName{get;set;} 9: public string StateName{get;set;} 10: }

    Html 代码

    1: ... 2: ... 3: ... 4: <input type="text" name="FName"> 5: <input type="text" name="address.CityName"> 6: <input type="text" name="address.StateName"> 7: ... 8: ... 9: ...

    实验11——重置按钮和取消按钮

    1. 添加重置和取消按钮

    1: ... 2:   3: ... 4:   5: ... 6:   7: <input type="submit" name="BtnSubmit” value="Save Employee" /> 8:   9: <input type="button" name="BtnReset" value="Reset" οnclick="ResetForm();" /> 10:   11: <input type="submit" name="BtnSubmit" value="Cancel" />

    2. 定义 ResetForm 函数 

    在Html的头部分添加脚本标签,并编写JavaScript 函数 命名为”ResetForm“如下:

    1: <script> 2: function ResetForm() { 3: document.getElementById('TxtFName').value = ""; 4: document.getElementById('TxtLName').value = ""; 5: document.getElementById('TxtSalary').value = ""; 6: } 7: </script>

    3. 在 EmplyeeController 的 SaveEmployee 方法中实现取消按钮的点击功能

    修改SaveEmployee  方法:

    1: public ActionResult SaveEmployee(Employee e, string BtnSubmit) 2: { 3: switch (BtnSubmit) 4: { 5: case "Save Employee": 6: return Content(e.FirstName + "|" + e.LastName + "|" + e.Salary); 7: case "Cancel": 8: return RedirectToAction("Index"); 9: } 10: return new EmptyResult(); 11: }

    4.  运行

    5. 测试重置功能

    id="iframe_0.7476919191651075" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.codeproject.com/KB/aspnet/986730/M.9.png?_=0.38615635736131715%22%20style=%22border:none;max-width:711px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.7476919191651075',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 477px; height: 213px;">

    6. 测试保存和取消功能

    id="iframe_0.9997461150945062" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.codeproject.com/KB/aspnet/986730/N.10.png?_=0.3109612917952489%22%20style=%22border:none;max-width:711px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.9997461150945062',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 602px; height: 246px;">

    关于实验11

    在实验11中为什么将保存和取消按钮设置为同名?

    在日常使用中,点击提交按钮之后,请求会被发送到服务器端,所有输入控件的值都将被发送。提交按钮也是输入按钮的一种。因此提交按钮的值也会被发送。

    当保存按钮被点击时,保存按钮的值也会随着请求被发送到服务器端,当点击取消按钮时,取消按钮的值”取消“会随着请求发送。

    在Action 方法中,Model Binder 将维护这些工作。会根据接收到的值更新参数值。

    实现多重提交按钮有没有其他可用的方法?

    事实上,有很多可实现的方法。以下会介绍三种方法。

    1. 隐藏 Form 元素

       在View中创建一个隐藏form元素 1: <form action="/Employee/CancelSave" id="CancelForm" method="get" style="display:none"> 2:   3: </form>    将提交按钮改为正常按钮,并且使用JavaScript脚本代码: 1: <input type="button" name="BtnSubmit" value="Cancel" οnclick="document.getElementById('CancelForm').submit()" />   2. 使用JavaScript 动态的修改URL   1: <form action="" method="post" id="EmployeeForm" > 2: ... 3: ... 4: <input type="submit" name="BtnSubmit" value="Save Employee" οnclick="document.getElementById('EmployeeForm').action = '/Employee/SaveEmployee'" /> 5: ... 6: <input type="submit" name="BtnSubmit" value="Cancel" οnclick="document.getElementById('EmployeeForm').action = '/Employee/CancelSave'" /> 7: </form>

    3. Ajax

    使用常规输入按钮来代替提交按钮,并且点击时使用jQuery或任何其他库来产生纯Ajax请求。

    为什么在实现重置功能时,不使用 input type=reset ?

    因为输入类型type=reset 不是清晰的值,仅设置了控件的默认值。如:

    1: <input type="text" name="FName" value="Sukesh">

    在该实例中控件值为:Sukesh,如果使用type=reset来实现重置功能,当重置按钮被点击时,textbox的值会被设置为”Sukesh“。

    如果控件名称与类属性名称不匹配会发生什么情况?

    首先来看一段HTML代码:

    1: First Name: <input type="text" id="TxtFName" name="FName" value="" /><br /> 2: Last Name: <input type="text" id="TxtLName" name="LName" value="" /><br /> 3: Salary: <input type="text" id="TxtSalary" name="Salary" value="" /><br />

    Model 类包含属性名称如FirstName, LastName 和 Salary。由于默认的Model Binder在该片段内不会发生作用。

    我们会给出三种解决方案

    内部action 方法,获取请求中的post数据。Form 语法和手动构建Model对象: 1: public ActionResult SaveEmployee() 2: { 3: Employee e = new Employee(); 4: e.FirstName = Request.Form["FName"]; 5: e.LastName = Request.Form["LName"]; 6: e.Salary = int.Parse(Request.Form["Salary"]) 7: ... 8: ... 9: } 使用参数名称和手动创建Model对象: 1: public ActionResult SaveEmployee(string FName, string LName, int Salary) 2: { 3: Employee e = new Employee(); 4: e.FirstName = FName; 5: e.LastName = LName; 6: e.Salary = Salary; 7: ... 8: ... 9: } 创建自定义 Model Binder ,代替默认的Model Binder:

    1. 创建自定义Model Binder

    1: public class MyEmployeeModelBinder : DefaultModelBinder 2: { 3: protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) 4: { 5: Employee e = new Employee(); 6: e.FirstName = controllerContext.RequestContext.HttpContext.Request.Form["FName"]; 7: e.LastName = controllerContext.RequestContext.HttpContext.Request.Form["LName"]; 8: e.Salary = int.Parse(controllerContext.RequestContext.HttpContext.Request.Form["Salary"]); 9: return e; 10: } 11: }

    2. 替换默认的 Model Binder

    1: public ActionResult SaveEmployee([ModelBinder(typeof(MyEmployeeModelBinder))]Employee e, string BtnSubmit) 2: { 3: ......  

    RedirectToAction 函数的功能?

    RedirectToAction 生成 RedirectToRouteResult 如ViewResult 和 ContentResult,RedirectToRouteResult是 ActionResult的孩子节点,表示间接响应,当浏览器接收到RedirectToRouteResult,新Action 方法产生新的请求。

    EmptyResult是什么?

    是ActionResult的一个孩子节点,当浏览器接收到 EmptyResult,作为响应,它会显示空白屏幕,表示无结果。在本实验中不会发生EmptyResult。

    实验12——保存数据库记录,更新表格

    1. 在EmployeeBusinessLayer  中创建 SaveEmployee,如下:

    1: public Employee SaveEmployee(Employee e) 2: { 3: SalesERPDAL salesDal = new SalesERPDAL(); 4: salesDal.Employees.Add(e); 5: salesDal.SaveChanges(); 6: return e; 7: }

    2. 修改 SaveEmployee 的Action 方法

    在 EmployeeController 中修改 SaveEmployee ation方法 代码如下:

    1: public ActionResult SaveEmployee(Employee e, string BtnSubmit) 2: { 3: switch (BtnSubmit) 4: { 5: case "Save Employee": 6: EmployeeBusinessLayer empBal = new EmployeeBusinessLayer(); 7: empBal.SaveEmployee(e); 8: return RedirectToAction("Index"); 9: case "Cancel": 10: return RedirectToAction("Index"); 11: } 12: return new EmptyResult(); 13: }

    3.运行

    id="iframe_0.0506665548353924" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.codeproject.com/KB/aspnet/986730/O.11.png?_=0.10382265170249072%22%20style=%22border:none;max-width:711px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.0506665548353924',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 374px; height: 165px;">

    实验 13——添加服务器端验证

    在实验10中已经了解了Model Binder的基本功能,再来了解一下:

    Model Binder使用 post数据更新 Employee对象但是这不仅仅执行Model Binder的。Model Binder需要更新Model State。Model State封装了 Model状态。 ModelState包含属性IsValid ,该属性表示 Model 是否成功更新。如果任何服务器端验证失败,Model将不更新。

                  ModelState保存验证错误的详情。

                   如:ModelState[“FirstName”],表示将包含所有与First Name相关的错误。

                  保存接收的值(Post 数据或查询字符串的值)

                 在Asp.net MVC,将使用 DataAnnotations来执行服务器端的验证。

    在我们了解Data Annotation之前先来了解一些Model Binder知识:

    使用元数据类型时,Model Binder 是如何工作的?

    当Action方法包含元类型参数,Model Binder会与参数名称对比。

    当匹配成功时,响应接收的数据会被分配给参数。匹配不成功时,参数会设置为缺省值,例如,如果是字符串类型则被设置为null,如果是整型则设置为0.由于数据类型未匹配异常的抛出,不会进行值分配。

    当参数是类时,Model Binder 是如何工作的?

    当参数为类,Model Binder将通过检索类所有的属性,将接收的数据与类属性名称比较。

    当匹配成功时:

    如果接收的值是空,则会将空值分配给属性,如果无法执行空值分配,会设置缺省值,ModelState.IsValid将设置为fasle。

    如果空值分配成功,会考虑值是否合法,ModelState.IsValid将设置为fasle。

    如果匹配不成功,参数会被设置为缺省值。在本实验中ModelState.IsValid不会受影响。

    1. 使用 DataAnnotations 装饰属性

    1: public class Employee 2: { 3: ... 4: ... 5: [Required(ErrorMessage="Enter First Name")] 6: public string FirstName { get; set; } 7:   8: [StringLength(5,ErrorMessage="Last Name length should not be greater than 5")] 9: public string LastName { get; set; } 10: ... 11: ... 12: }

    2.  修改  SaveEmployee 方法

    1: public ActionResult SaveEmployee(Employee e, string BtnSubmit) 2: { 3: switch (BtnSubmit) 4: { 5: case "Save Employee": 6: if (ModelState.IsValid) 7: { 8: EmployeeBusinessLayer empBal = new EmployeeBusinessLayer(); 9: empBal.SaveEmployee(e); 10: return RedirectToAction("Index"); 11: } 12: else 13: { 14: return View("CreateEmployee "); 15: } 16: case "Cancel": 17: return RedirectToAction("Index"); 18: } 19: return new EmptyResult(); 20: }

    3. 在View中显示错误,修改 Views/Index/CreateEmployee.cshtml 代码,使用Table标签如下:

    1: <table> 2: <tr> 3: <td> 4: First Name: 5: </td> 6: <td> 7: <input type="text" id="TxtFName" name="FirstName" value="" /> 8: </td> 9: </tr> 10: <tr> 11: <td colspan="2" align="right"> 12: @Html.ValidationMessage("FirstName") 13: </td> 14: </tr> 15: <tr> 16: <td> 17: Last Name: 18: </td> 19: <td> 20: <input type="text" id="TxtLName" name="LastName" value="" /> 21: </td> 22: </tr> 23: <tr> 24: <td colspan="2" align="right"> 25: @Html.ValidationMessage("LastName") 26: </td> 27: </tr> 28:   29: <tr> 30: <td> 31: Salary: 32: </td> 33: <td> 34: <input type="text" id="TxtSalary" name="Salary" value="" /> 35: </td> 36: </tr> 37: <tr> 38: <td colspan="2" align="right"> 39: @Html.ValidationMessage("Salary") 40: </td> 41: </tr> 42:   43: <tr> 44: <td colspan="2"> 45: <input type="submit" name="BtnSubmit" value="Save Employee" /> 46: <input type="submit" name="BtnSubmit" value="Cancel" /> 47: <input type="button" name="BtnReset" value="Reset" οnclick="ResetForm();" /> 48: </td> 49: </tr> 50: </table>

    4. 运行测试

    导航到“Employee/AddNew” 方法,执行测试。

    测试1。

    id="iframe_0.911524557874352" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.codeproject.com/KB/aspnet/986730/P.12.png?_=0.2832464188886479%22%20style=%22border:none;max-width:711px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.911524557874352',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 437px; height: 157px;">

    测试2

    id="iframe_0.32923913714853614" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.codeproject.com/KB/aspnet/986730/Q.13.png?_=0.9778547875550325%22%20style=%22border:none;max-width:711px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.32923913714853614',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 498px; height: 152px;">

     

    注意:可能会出现以下错误:

    “The model backing the 'SalesERPDAL' context has changed since the database was created. Consider using Code First Migrations to update the database.”

    为了解决上述错误,在Global.asax文件中在 Application_Start后添加以下语句:

    1: Database.SetInitializer(new DropCreateDatabaseIfModelChanges<SalesERPDAL>());

    注意: 使用Database类需要引用 “System.Data.Entity”命名空间

    关于实验13

    @Html.ValidationMessage是什么意思?

    @符号表示是Razor代码Html是HtmlHelper类的实例。ValidationMessage是HtmlHelper类的函数,用来表示错误信息。

    ValidationMessage 函数是如何工作的?

    ValidationMessage 是运行时执行的函数。如之前讨论的,ModelBinder更新ModelState。ValidationMessage根据关键字显示ModelState表示的错误信息。

    如果我们需要可空的整数域,该怎么做?

    1: public int? Salary{get;set;}

    UpdateModel 和 TryUpdateModel 方法之间的区别是什么?

    TryUpdateModel 与UpdateModel 几乎是相同的,有点略微差别。

    如果Model调整失败,UpdateModel会抛出异常。就不会使用UpdateModel的 ModelState.IsValid属性。

    TryUpdateModel是将函数参数与Employee对象保持相同,如果更新失败,ModelState.IsValid会设置为False值。

    客户端验证是什么?

    客户端验证是手动执行的,除非使用HTML 帮助类。我们将在下一节介绍HTML 帮助类。

    实验14——自定义服务器端验证

    1. 创建自定义验证

    新建类,并命名为FirstNameValidation,代码如下:

    1: public class FirstNameValidation:ValidationAttribute 2: { 3: protected override ValidationResult IsValid(object value, ValidationContext validationContext) 4: { 5: if (value == null) // Checking for Empty Value 6: { 7: return new ValidationResult("Please Provide First Name"); 8: } 9: else 10: { 11: if (value.ToString().Contains("@")) 12: { 13: return new ValidationResult("First Name should contain @"); 14: } 15: } 16: return ValidationResult.Success; 17: } 18: }

    2. 附加到First Name

    打开Employee类,删除FirstName的默认的Required属性,添加FirstNameValidation,代码如下:

    1: [FirstNameValidation] 2: public string FirstName { get; set; }

    3. 运行

    导航到Employee/AddNew

    测试1:

    id="iframe_0.7166504437798324" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.codeproject.com/KB/aspnet/986730/r.14.png?_=0.23511973549446674%22%20style=%22border:none;max-width:711px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.7166504437798324',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 426px; height: 147px;">

    测试2:

    id="iframe_0.35427769188035874" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.codeproject.com/KB/aspnet/986730/s.15.png?_=0.561685304912384%22%20style=%22border:none;max-width:711px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.35427769188035874',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 426px; height: 154px;">

    结论

    本节主要讲解了数据访问层相关的知识,如数据验证,数据更新,数据处理,form表单的使用等。

    在下一章,我们主要讲述以下内容:

    实现客户端验证理解HTML 帮助类实现认证功能给部分View添加页脚使用母版页创建统一的布局自定义请求过滤器                   

    数据处理是MVC学习中比较重要的部分,在完成本节学习的同时,你是否对MVC的理解又进了一步?知识能够帮助我们更好的进行MVC开发,开发工具一样能够大幅度提升开发效率。使用 ComponentOne Studio ASP.NET MVC 这款轻量级控件,在效率大幅提高的同时,还能满足用户的所有需求。

    转载请注明原文地址: https://ju.6miu.com/read-1203507.html
    最新回复(0)