7 天学习 MVC 项目 - 第 3 天






4.92/5 (174投票s)
在本文中,我们将通过引入数据录入屏幕和数据库通信,将我们的MVC 5项目提升到新的水平。
引言
我们假设您在阅读本文之前已经阅读了前几天的内容。在第2天,我们完成了一个包含员工列表网格的项目。
在第3天,我们将通过引入数据访问层和数据录入屏幕将其提升到新的水平。
完整系列
我们很高兴地宣布,本文现已发行纸质版书籍,您可以从www.amazon.com和www.flipkart.com购买。
议程
数据访问层
一个真实的实时项目没有数据库是不完整的。在我们的项目中,我们尚未讨论数据库层。第3天的第一个实验将全部关于数据库和数据库层。
在这里,我们将分别使用Sql Server和Entity Framework创建数据库和数据库访问层。
简单来说,什么是Entity Framework?
它是一个ORM工具。ORM代表对象关系映射。
在RDBMS世界中,我们以表和列来谈论,而在.net世界(这是一个面向对象的世界)中,我们以类、对象和属性来谈论。
当我们考虑任何数据驱动的应用程序时,最终会得到以下两件事。
- 编写与数据库通信的代码(称为数据访问层或数据库逻辑)
- 编写将数据库数据映射到面向对象数据或反之的代码。
ORM是一个可以自动化这两件事的工具。Entity Framework是微软的ORM工具。
什么是Code First方法?
在Entity Framework中,我们可以遵循以下三种方法之一
- Database First方法 – 创建包含表、列、关系等的数据库,Entity Framework将生成相应的模型类(业务实体)和数据访问层代码。
- Model First方法 – 在这种方法中,模型类及其之间的关系将使用Visual Studio中的模型设计器手动定义,Entity Framework将自动生成数据访问层和包含表、列、关系的数据库。
- Code First方法 – 在这种方法中,将手动创建POCO类。这些类之间的关系将通过代码定义。当应用程序第一次执行时,Entity Framework将自动在数据库服务器中生成数据访问层和包含表、列和关系的数据库。
POCO类是什么意思?
POCO代表“Plain Old CLR objects”。POCO类指的是我们创建的简单.Net类。在我们之前的示例中,Employee类就是一个简单的POCO类。
实验8 – 向项目添加数据访问层
步骤1 – 创建数据库
连接到Sql Server并创建一个名为“SalesERPDB”的新数据库。
步骤2 – 创建连接字符串
打开Web.config文件,并在Configuration节中添加以下节
<connectionStrings> <add connectionString="Data Source=(local);Initial Catalog=SalesERPDB;Integrated Security=True" name="SalesERPDAL" providerName="System.Data.SqlClient"/> </connectionStrings>
步骤3 – 添加Entity Framework引用
右键单击项目 >> 管理Nuget包。搜索Entity Framework并单击安装。
步骤4 – 创建数据访问层。
- 在根文件夹中创建一个名为“DataAccessLayer”的新文件夹,并在其中创建一个名为“SalesERPDAL”的新类。
-
在顶部添加using语句,如下所示。
-
using System.Data.Entity;
- 从DbContext继承“SalesERPDAL”
public class SalesERPDAL: DbContext
{
}
步骤5 – 为员工类创建主键字段
打开Employee类,并在顶部添加using语句,如下所示。
using System.ComponentModel.DataAnnotations;
在Employee类中添加EmployeeId属性并将其标记为Key属性。
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语句
using WebApplication1.Models;
重写SalesERPDAL类中的OnModelCreating方法,如下所示。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity().ToTable("TblEmployee");
base.OnModelCreating(modelBuilder);
}
注意:在上面的代码片段中,“TblEmployee”代表表名。它会在运行时自动创建。
步骤7 – 创建用于在数据库中保存员工的属性
在“SalesERPDAL”类中创建一个名为Employee的新属性,如下所示
public DbSet Employees{get;set;}
DbSet将表示可以从数据库查询的所有员工。
步骤8 – 更改业务层代码并从数据库获取数据
打开EmployeeBusinessLayer类。在顶部添加using语句。
using WebApplication1.DataAccessLayer;
现在将GetEmployees方法类更改如下。
public List GetEmployees()
{
SalesERPDAL salesDal = new SalesERPDAL();
return salesDal.Employees.ToList();
}
步骤 9 – 执行并测试
按 F5 并执行应用程序。
目前数据库中没有任何员工,所以我们将看到一个空白网格。
检查数据库。现在我们有一个名为TblEmployee的表,其中包含所有列。
步骤9 – 插入测试数据
向TblEmployee表中添加一些虚拟数据。
步骤10 – 执行并测试应用程序
按F5并再次运行应用程序。
开始了 :)
关于实验8的讲解
什么是DbSet?
DbSet简单地表示可以从数据库查询的所有实体的集合。当我们对DbSet对象编写Linq查询时,它在内部被转换为查询并针对数据库执行。
在我们的例子中,“Employees”是一个DbSet,它将保存所有可以从数据库查询的“Employee”实体。每次我们尝试访问“Employees”时,它都会获取“TblEmployee”表中的所有记录,并将其转换为“Employee”对象并返回集合。
连接字符串和数据访问层是如何连接的?
映射将根据名称进行。在我们的例子中,ConnectionString名称和数据访问层类名相同,即“SalesERPDAL”,因此自动映射。
我们可以更改连接字符串名称吗?
是的,在这种情况下,我们必须在数据访问层类中定义一个构造函数,如下所示。
public SalesERPDAL():base("NewName")
{
}
整理所有内容
为了使所有内容组织有序且有意义,让我们做一些更改。
步骤1 - 重命名
- “TestController”到“EmployeeController”
- GetView操作方法到Index
- Test文件夹(在Views文件夹中)到Employee
- 并将“MyView”视图重命名为“Index”
步骤2 – 从EmployeeListViewModel中删除UserName属性
步骤3 – 从视图中删除UserName
打开Views/Employee.Index.cshtml视图并从中删除用户名。
简单来说,删除以下代码块。
Hello @Model.UserName <hr />
步骤2 – 更改EmployeeController中的Index操作方法
相应地更改EmployeeController中Index操作中的代码,如下所示。
public ActionResult Index()
{
……
……
……
employeeListViewModel.Employees = empViewModels;
//employeeListViewModel.UserName = "Admin";-->Remove this line -->Change1
return View("Index", employeeListViewModel);//-->Change View Name -->Change 2
}
现在执行时URL将是“…./Employee/Index”
实验9 – 创建数据录入屏幕
步骤1 – 创建动作方法
在EmployeeController中创建一个名为“AddNew”的动作方法,如下所示
public ActionResult AddNew()
{
return View("CreateEmployee");
}
步骤2 – 创建视图
在View/Employee文件夹中创建一个名为“CreateEmployee”的视图,如下所示。
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>CreateEmployee</title> </head> <body> <div> <form action="/Employee/SaveEmployee" method="post"> First Name: <input type="text" id="TxtFName" name="FirstName" value="" /><br /> Last Name: <input type="text" id="TxtLName" name="LastName" value="" /><br /> Salary: <input type="text" id="TxtSalary" name="Salary" value="" /><br /> <input type="submit" name="BtnSave" value="Save Employee" /> <input type="button" name="BtnReset" value="Reset" /> </form> </div> </body> </html>
步骤3 – 在Index视图中创建链接
打开Index.cshtml并添加一个指向AddNew Action URL的超链接。
<ahref="/Employee/AddNew">Add New</a>
步骤4 – 执行并测试应用程序
按F5并执行应用程序
关于实验9的讲解
表单标签的目的是什么?
在本系列的第一天,我们了解到“Web世界不遵循事件驱动的编程模型。它遵循请求-响应模型。最终用户发出请求,服务器发送响应。” 表单标签是HTML中发出请求的一种方式。一旦表单标签内的提交按钮被点击,请求就会发送到action属性中指定的URL。
表单标签中的method属性是什么?
它决定请求的类型。请求可能是以下四种类型之一:get、post、put和delete。
根据Web标准,我们应该使用 –
- Get -> 当我们想要获取某些东西时
- Post -> 当我们想要创建某些东西时
- Put -> 当我们想要更新某些东西时
- Delete -> 当我们想要删除某些东西时。
使用Form标签发出请求与通过浏览器地址栏或超链接发出请求有何不同?
当使用Form标签发出请求时,所有输入控件的值都会随请求一起发送以进行处理。
复选框、单选按钮和下拉列表呢?这些控件的值也会发送吗?
是的,所有输入控件(input type=text, type=radio, type=checkbox)以及下拉列表(表示为“Select”元素)。
值如何发送到服务器?
当请求类型为Get、Put或Delete时,值将作为查询字符串参数发送。
当它是post请求时,值将作为posted数据发送。
输入控件中name属性的目的是什么?
如前所述,当提交按钮被点击时,所有输入控件的值将随请求一起发送。这使得服务器一次接收多个值。为了在发送时区分每个值,每个值都附加一个键,这个键就是“name”属性。
name和id属性的作用相同吗?
不,根据上一个问题,“name”属性将由HTML在发送请求时内部使用,而“id”属性将由开发人员在JavaScript中用于一些动态操作。
“input type=submit”和“input type=button”有什么区别?
提交按钮专门用于向服务器发出请求,而简单按钮用于执行一些自定义客户端操作。简单按钮本身不会做任何事情。
实验10 – 在服务器端/控制器中获取提交的数据
步骤1 – 创建SaveEmployee Action方法
在Employee Controller中创建一个名为SaveEmployee的动作方法,如下所示。
public string SaveEmployee(Employee e)
{
return e.FirstName + "|"+ e.LastName+"|"+e.Salary;
}
步骤2 – 执行并测试
按 F5 并执行应用程序。
关于实验10的讲解
动作方法中的Employee对象如何更新文本框值?
在Asp.Net MVC中有一个叫做Model Binder的概念。
- 当请求包含参数的动作方法时,Model Binder将自动执行。
- 模型绑定器将遍历方法的所有基本参数,然后将参数名称与传入数据中的每个键进行比较(传入数据意味着已发布数据或查询字符串)。找到匹配项时,相应的传入数据将分配给参数。
- 之后,模型绑定器将遍历每个类参数的每个属性,并将每个属性名称与传入数据中的每个键进行比较。找到匹配项时,相应的传入值将分配给参数。
如果指定了两个参数,一个为“Employee e”,另一个为“string FirstName”,会发生什么?
FirstName将同时在原始的FirstName变量和e.FirstName属性中更新。
模型绑定器会与组合关系一起工作吗?
是的,它会,但在那种情况下,控件的名称应相应地给出。
示例
假设我们有Customer类和Address类,如下所示
public class Customer
{
public string FName{get;set;}
public Address address{get;set;}
}
public class Address
{
public string CityName{get;set;}
public string StateName{get;set;}
}
在这种情况下,Html应该看起来像这样
... ... ... <input type="text" name="FName"> <input type="text" name="address.CityName"> <input type="text" name="address.StateName"> ... ... ...
实验11 – 重置和取消按钮
步骤1 – 从重置和取消按钮开始
添加一个重置和取消按钮,如下所示
...
...
...
<input type="submit" name="BtnSubmit” value="Save Employee" />
<input type="button" name="BtnReset" value="Reset" onclick="ResetForm();" />
<input type="submit" name="BtnSubmit" value="Cancel" />
注意:保存按钮和取消按钮具有相同的“Name”属性值,即“BtnSubmit”。
步骤2 – 定义ResetForm函数
在Html的Head部分添加一个script标签,并在其中创建一个名为ResetForm的JavaScript函数,如下所示。
<script> function ResetForm() { document.getElementById('TxtFName').value = ""; document.getElementById('TxtLName').value = ""; document.getElementById('TxtSalary').value = ""; } </script>
步骤3 – 在EmplyeeController的SaveEmployee动作方法中实现取消点击。
将SaveEmployee动作方法更改如下
public ActionResult SaveEmployee(Employee e, string BtnSubmit)
{
switch (BtnSubmit)
{
case "Save Employee":
return Content(e.FirstName + "|" + e.LastName + "|" + e.Salary);
case "Cancel":
return RedirectToAction("Index");
}
return new EmptyResult();
}
步骤4 – 执行应用程序。
按F5并执行应用程序。通过点击“新增”链接导航到新增屏幕。
步骤5 – 测试重置功能
步骤6 – 测试保存和取消功能
关于实验11的讲解
为什么保存和取消按钮都使用相同的名称?
我们知道,一旦点击提交按钮,请求就会发送到服务器。所有输入控件的值也会随请求一起发送。
提交按钮也是一个输入按钮。因此,提交按钮的值(负责请求)也会发送。
当点击保存按钮时,保存按钮的值,即“保存员工”将随请求发送;当点击取消按钮时,取消按钮的值,即“取消”将随请求发送。
在Action方法中,Model Binder将完成剩余的工作。它将使用输入数据中的值(随请求传入)更新参数值。
实现多个提交按钮的其他方法有哪些?
有很多方法。我想讨论其中三种。
1. 隐藏表单元素
步骤1 – 在视图中创建一个隐藏的表单元素,如下所示。
<form action="/Employee/CancelSave" id="CancelForm" method="get" style="display:none"> </form>
<form action="/Employee/CancelSave" id="CancelForm" method="get" style="display:none"> </form>
步骤2 – 将提交按钮更改为普通按钮,并借助JavaScript提交上述表单。
<input type="button" name="BtnSubmit" value="Cancel" onclick="document.getElementById('CancelForm').submit()" />
2. 使用JavaScript动态更改动作URL
<form action="" method="post" id="EmployeeForm" > ... ... <input type="submit" name="BtnSubmit" value="Save Employee" onclick="document.getElementById('EmployeeForm').action = '/Employee/SaveEmployee'" /> ... <input type="submit" name="BtnSubmit" value="Cancel" onclick="document.getElementById('EmployeeForm').action = '/Employee/CancelSave'" /> </form>
3. Ajax
使用简单的输入按钮代替提交按钮,并在其onclick事件中使用jQuery或任何其他库发出纯Ajax请求。
为什么我们没有使用input type=reset来实现重置功能?
input type=reset 控件不会清除值,它只是将值设置为控件的默认值。例如
<input type="text" name="FName" value="Sukesh">
在上面的示例中,控件的默认值为“Sukesh”。
如果使用 input type=reset 来实现重置功能,那么每次点击“reset”按钮时,文本框中将默认设置为“Sukesh”。
如果名称与类的属性名称不匹配会怎样?
这是一个非常常见的面试问题。
假设我们有以下HTML
First Name: <input type="text" id="TxtFName" name="FName" value="" /><br /> Last Name: <input type="text" id="TxtLName" name="LName" value="" /><br /> Salary: <input type="text" id="TxtSalary" name="Salary" value="" /><br />
现在我们的模型类包含FirstName、LastName和Salary等属性名称。因此,默认模型绑定器在这里将不起作用。
在这种情况下,我们有以下三种解决方案
- 在动作方法内部,使用Request.Form语法检索提交的值,并手动构造模型对象,如下所示。
public ActionResult SaveEmployee()
{
Employee e = new Employee();
e.FirstName = Request.Form["FName"];
e.LastName = Request.Form["LName"];
e.Salary = int.Parse(Request.Form["Salary"])
...
...
}
- 使用参数名称并手动创建模型对象,如下所示。
public ActionResult SaveEmployee(string FName, string LName, int Salary)
{
Employee e = new Employee();
e.FirstName = FName;
e.LastName = LName;
e.Salary = Salary;
...
...
}
- 创建自定义模型绑定器并替换默认模型绑定器,如下所示。
步骤1 – 创建自定义模型绑定器
public class MyEmployeeModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
Employee e = new Employee();
e.FirstName = controllerContext.RequestContext.HttpContext.Request.Form["FName"];
e.LastName = controllerContext.RequestContext.HttpContext.Request.Form["LName"];
e.Salary = int.Parse(controllerContext.RequestContext.HttpContext.Request.Form["Salary"]);
return e;
}
}
步骤2 – 用这个新的模型绑定器替换默认的模型绑定器
public ActionResult SaveEmployee([ModelBinder(typeof(MyEmployeeModelBinder))]Employee e, string BtnSubmit)
{
......
RedirecttToFunction有什么作用?
它生成RedirectToRouteResult,就像ViewResult和ContentResult一样(在第一天讨论过),RedirectToRouteResult是ActionResult的子类。它表示重定向响应。当浏览器收到RedirectToRouteResult时,它会向新的动作方法发出新的请求。
注意:这里浏览器负责新的请求,因此URL将更新为新的URL。
什么是EmptyResult?
ActionResult的另一个子类。当浏览器收到EmptyResult作为响应时,它只是显示空白屏幕。它简单地表示“没有结果”。
在我们的示例中,这种情况不会发生。为了确保所有代码路径都返回一个值,因此编写了EmptyResult语句。
注意:当ActionMethod返回类型为Void时,它等同于EmptyResult
实验12 – 将记录保存到数据库并更新网格
步骤1 – 在EmployeeBusinessLayer中创建SaveEmployee,如下所示
public Employee SaveEmployee(Employee e)
{
SalesERPDAL salesDal = new SalesERPDAL();
salesDal.Employees.Add(e);
salesDal.SaveChanges();
return e;
}
步骤2 – 更改SaveEmployee Action方法
在EmployeeController中更改SaveEmployee动作方法代码,如下所示。
public ActionResult SaveEmployee(Employee e, string BtnSubmit)
{
switch (BtnSubmit)
{
case "Save Employee":
EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();
empBal.SaveEmployee(e);
return RedirectToAction("Index");
case "Cancel":
return RedirectToAction("Index");
}
return new EmptyResult();
}
步骤 3 – 执行和测试
按F5并执行应用程序。导航到数据录入屏幕并输入一些有效值。
实验13 – 添加服务器端验证
在实验10中,我们看到了模型绑定器的基本功能。让我们进一步了解它。
- 模型绑定器用提交的数据更新Employee对象。
- 但这并不是模型绑定器唯一执行的功能。模型绑定器还会更新ModelState。ModelState封装了模型的状态。
- 它有一个名为IsValid的属性,用于确定模型(即Employee对象)是否成功更新。如果任何服务器端验证失败,模型将不会更新。
- 它保存验证错误。
示例:ModelState["FirstName "].Errors将包含与FirstName相关的所有错误- 它保存传入值(提交的数据或查询字符串数据)
- 它保存传入值(提交的数据或查询字符串数据)
在Asp.net MVC中,我们使用DataAnnotations执行服务器端验证。
在我们深入了解数据注释之前,让我们先了解一下模型绑定器的一些其他内容
模型绑定器如何与基本数据类型配合使用
当动作方法包含原始类型参数时,模型绑定器会将参数名称与传入数据中的每个键进行比较(传入数据表示提交的数据或查询字符串)。
- 当找到匹配项时,相应的传入数据将分配给参数。
- 如果未找到匹配项,则参数将分配默认值。(默认值 – 对于整数为0,对于字符串为null等)
- 如果由于数据类型不匹配而无法分配,将抛出异常。
模型绑定器如何与类配合使用
当参数是类参数时,模型绑定器将遍历所有类的所有属性,并将每个属性名称与传入数据中的每个键进行比较。
- 当找到匹配项时,
- 如果相应的传入值为空,则
- 将为属性分配空值。如果无法分配空值,将设置默认值,并且ModelState.IsValid将设置为false。
- 如果允许为空赋值,但由于属性上的验证而将其视为无效值,则将为空赋值,并且ModelState.IsValid将设置为false。
- 如果相应的传入值不为空,
- 如果由于数据类型不匹配或服务器端验证失败而无法分配,将分配空值,并将ModelState.IsValid设置为false。
- 如果无法分配空值,则将设置默认值
- 当未找到匹配项时,参数将分配默认值。(默认值——对于整数为0,对于字符串为null等)在这种情况下,ModelState.IsValid将不受影响。
让我们通过向正在进行的项目添加验证功能来理解这一点。
步骤1 – 用DataAnnotations修饰属性
打开Model文件夹中的Employee类,并使用DataAnnotation属性修饰FirstName和LastName属性,如下所示。
public class Employee
{
...
...
[Required(ErrorMessage="Enter First Name")]
public string FirstName { get; set; }
[StringLength(5,ErrorMessage="Last Name length should not be greater than 5")]
public string LastName { get; set; }
...
...
}
步骤2 – 更改SaveEmployee Action方法
打开EmplyeeController并更改SaveEmployee Action方法,如下所示。
public ActionResult SaveEmployee(Employee e, string BtnSubmit)
{
switch (BtnSubmit)
{
case "Save Employee":
if (ModelState.IsValid)
{
EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();
empBal.SaveEmployee(e);
return RedirectToAction("Index");
}
else
{
return View("CreateEmployee ");
}
case "Cancel":
return RedirectToAction("Index");
}
return new EmptyResult();
}
注意:如您所见,当ModelState.IsValid为false时,SaveEmployee按钮点击的响应是指向“CreateEmployee”视图的ViewResult。
步骤3 – 在视图中显示错误
将“Views/Index/CreateEmployee.cshtml”中的HTML更改为以下内容。
这次我们将借助“table”标签稍微格式化我们的UI;
<table> <tr> <td> First Name: </td> <td> <input type="text" id="TxtFName" name="FirstName" value="" /> </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="" /> </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="" /> </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" /> <input type="submit" name="BtnSubmit" value="Cancel" /> <input type="button" name="BtnReset" value="Reset" onclick="ResetForm();" /> </td> </tr> </table>
步骤4 – 执行并测试
按F5并执行应用程序。导航到“Employee/AddNew”动作方法并测试应用程序。
测试1
测试2
注意:您可能会遇到以下错误。
“自创建数据库以来,支持'SalesERPDAL'上下文的模型已更改。考虑使用Code First Migrations更新数据库。”
要消除此错误,只需在Global.asax文件中的Application_Start中添加以下语句。
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<SalesERPDAL>());
Database类存在于System.Data.Entity命名空间中
如果您仍然遇到相同的错误,请在Sql server中打开数据库并删除__MigrationHistory表。
很快我将发布一个关于Entity Framework的新系列,我们将逐步学习Entity Framework。本系列旨在介绍MVC,我们将努力坚持这一点。
关于实验13的讲解
@Html.ValidationMessage有什么作用?
- @表示它是Razor代码
- Html是视图中可用的HtmlHelper类的实例。
- ValidationMessage是HtmlHelper类的一个函数,用于显示错误消息。
ValidationMessage函数是如何工作的?
ValidationMessage是一个函数。它在运行时执行。正如我们之前讨论的,ModelBinder更新ModelState。ValidationMessage根据Key从ModelState显示错误消息。
示例:ValidationMessage(“FirstName”)显示与FirstName相关的错误消息。
我们还有其他像required和StringLength这样的属性吗?
是的,这里有一些
- DataType – 确保数据是特定类型,如电子邮件、信用卡号、URL等。
- EnumDataTypeAttribute – 确保值存在于枚举中。
- Range Attribute – 确保值在特定范围内。
- Regular expression – 根据特定的正则表达式验证值。
- Required – 确保提供了值。
- StringthLength – 验证字符串的最大和最小字符数。
Salary是如何被验证的?
我们没有为Salary属性添加任何Data Annotation属性,但它仍然被验证。原因是,模型绑定器在更新模型时也会考虑属性的数据类型。
在测试1中 – 我们将薪资设置为空字符串。在这种情况下,根据我们之前(在实验13中)的模型绑定器解释,ModelState.IsVaid将为false,ModelState将保存与薪资相关的验证错误,这些错误将由于Html.ValidationMessage(“Salary”)而在视图中显示。
在测试2中 – 薪资数据类型不匹配,因此验证失败。
这是否意味着,整数属性默认是强制性的?
是的,不仅是整数,所有值类型都是如此,因为它们不能持有空值。
如果我们想要一个非必需的整数字段怎么办?
让它可为空?
public int? Salary{get;set;}
如何更改为Salary指定的验证消息?
Salary的默认验证支持(由于int数据类型)不允许我们更改验证消息。我们可以通过使用自己的验证(如正则表达式、范围或自定义验证器)来实现相同的功能。
为什么验证失败时值会被清除?
因为这是一个新的请求。最初呈现和后来呈现的DataEntry视图从开发角度看是相同的,但从请求角度看是不同的。我们将在第4天学习如何维护值。
我们可以明确要求模型绑定器执行吗?
是的,只需从动作方法中删除参数即可。它默认阻止默认模型绑定器执行。
在这种情况下,我们可以使用UpdateModel函数,如下所示。
Employee e = new Employee();
UpdateModel(e);
注意:UpdateModel不适用于基本数据类型。
UpdateModel和TryUpdateModel方法有什么区别?
TryUpdateModel与UpdateModel相同,但有一个额外的优势。
如果模型适配因任何原因失败,UpdateModel将抛出异常。在UpdateModel函数的情况下,ModelState.IsValid将没有任何用处。
TryUpdateModel与将Employee对象作为函数参数完全相同。如果更新失败,ModelState.IsValid将为false;
客户端验证呢?
除非我们使用HTML Helper类,否则应手动完成。
我们将在第4天讨论手动客户端验证和借助HTML Helper类的自动客户端验证。
我们可以将多个DataAnnotation属性附加到同一个属性吗?
是的,我们可以。在这种情况下,两种验证都会触发。
实验14 – 自定义服务器端验证
步骤1 – 创建自定义验证
打开Employee.cs文件,并在其中创建一个名为FirstNameValidation的新类,如下所示。
public class FirstNameValidation:ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null) // Checking for Empty Value
{
return new ValidationResult("Please Provide First Name");
}
else
{
if (value.ToString().Contains("@"))
{
return new ValidationResult("First Name should Not contain @");
}
}
return ValidationResult.Success;
}
}
注意:在单个文件中创建多个类从来不被认为是一个好习惯。因此,在您的示例中,我建议您在根位置创建一个名为“Validations”的新文件夹,并在其中创建一个新类。
步骤2 – 将其附加到FirstName
打开Employee类,删除FirstName属性的默认“Required”属性,并附加FirstNameValidation,如下所示。
[FirstNameValidation]
public string FirstName { get; set; }
步骤 3 – 执行和测试
按F5。导航到“Employee/AddNew”动作。
测试1
测试2
结论
到此,我们完成了第3天。在第4天,我们将把项目带入下一个版本。这是第4天的议程
- 实现客户端验证
- 理解HTML helper
- 实现身份验证
- 使用局部视图添加页脚
- 使用母版页创建一致的布局
- 自定义请求筛选
在Facebook、LinkedIn或twitter上关注我们,以获取新版本的更新。
如需在孟买进行线下技术培训,请访问 StepByStepSchools.Net
如需在线培训,请访问JustCompile.com