65.9K
CodeProject 正在变化。 阅读更多。
Home

7 天学习 MVC 项目 - 第 3 天

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (174投票s)

2015年4月29日

CPOL

19分钟阅读

viewsIcon

766578

downloadIcon

23267

在本文中,我们将通过引入数据录入屏幕和数据库通信,将我们的MVC 5项目提升到新的水平。

引言

我们假设您在阅读本文之前已经阅读了前几天的内容。在第2天,我们完成了一个包含员工列表网格的项目。
在第3天,我们将通过引入数据访问层和数据录入屏幕将其提升到新的水平。

完整系列

我们很高兴地宣布,本文现已发行纸质版书籍,您可以从www.amazon.comwww.flipkart.com购买。

议程

数据访问层

简单来说,什么是Entity Framework?

什么是Code First方法?

实验8 – 向项目添加数据访问层

关于实验8的讲解

整理所有内容

实验9 – 创建数据录入屏幕

关于实验9的讲解

实验10 – 在服务器端/控制器中获取提交的数据

关于实验10的讲解

实验11 – 重置和取消按钮

关于实验11的讲解

实验12 – 将记录保存到数据库并更新网格

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

模型绑定器如何与基本数据类型配合使用

模型绑定器如何与类配合使用

关于实验13的讲解

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

结论

数据访问层

一个真实的实时项目没有数据库是不完整的。在我们的项目中,我们尚未讨论数据库层。第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
  • 实现身份验证
  • 使用局部视图添加页脚
  • 使用母版页创建一致的布局
  • 自定义请求筛选

FacebookLinkedIntwitter上关注我们,以获取新版本的更新。

如需在孟买进行线下技术培训,请访问 StepByStepSchools.Net
如需在线培训,请访问JustCompile.com

© . All rights reserved.