如何在智能告警平台CA触发测试告警
642
2022-09-13
7 天玩转 ASP.NET MVC — 第 3 天(7月14日是什么节日)
目录
0. 前言
我们假定你在开始学习时已经阅读了前两天的学习内容。在第 2 天我们完成了关于显示 Employees 列表的项目。
在第三天,我们将会通过介绍数据访问层和数据入口将它升级到一个新的层次。
1. 数据访问层
在真实场景的项目中,如果没有 Database,那么这个项目是未完成的。在我们的项目中,我们还没有谈到数据库。第三天的首个 Lab 将会学习数据库和数据库层。
这里我们将使用 SQL Server 和 Entity Framework 来创建 Database 和 Database 访问层。
简单来说,什么是 Entity Framework?
这是一个 ORM 工具。ORM 代表的是 Object Relational Mapping。即:对象关系映射。
在 RDBMS 领域中,我们所谈论的 Tables 表格和 Columns 列的这些方面,在 .NET 领域(面向对象领域)中被称为 Classes 类,对象和属性。
当我们思考任何有关数据驱动应用的方式时,都可以得出以下两种方式:
书写代码来和数据库打交道(被称为数据访问层和数据库逻辑)书写代码来将数据库数据映射到面向对象中,反之亦然。
ORM 是一个工具,可以自动做如上两件事。Entity Framework 是微软的 ORM 工具。
什么是 Code First 方法?
在 Entity Framework 中,我们可以使用如下三种的任意方法:
什么是 POCO 类?
POCO 代表的是「Plain Old CLR Objects」。POCO 类代表的是我们所创建的简单 .NET 类。在我们之前的例子中, Employee 类是一个简单的 POCO。
2. Lab 8 — 向项目中添加数据访问层
第一步:创建数据库
连接 SQL Server 然后创建一个新的数据库,命名为「SalesERPDB」。
第二步:创建 ConnectionString
打开 Web.config 文件,然后在 Configuration 区域内添加如下片段:
第三步:添加 Entity Framework 引用
第四步:创建数据访问层
public class SalesERPDAL: DbContext {}
第五步:为 Employee 类创建主键
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; }}
第六步:定义映射
using WebApplication1.Models;
在 SalesERPDAL 类中重写 OnModelCreating 方法。
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity
注意:上述代码中的片段「TblEmployee」代表的是表名。在运行时讲自动被创建。
第七步:在数据库中创建 Employees 属性
在「SalesERPDAL」类中创建一个新属性,命名为 Employee,如下所示:
public DbSet
DbSet 将会展示所有可以在数据库中查询到的 Employees。
第八步:改变业务层代码,从数据库中读取数据
using WebApplication1.DataAccessLayer;
现在改变 GetEmployees 方法如下:
public List
第九步:执行并测试
按下 F5,并执行应用。
现在的数据库中,我们没有任何的 Employees,所以我们看见的是一个空白的 Grid。
查看数据库,现在我们可以在 TblEmployee 表中看到所有的列。
第十步:插入测试数据
向 TblEmployee 表中插入一些测试数据。
第十一步:执行并测试应用
按下 F5 并再次运行应用。
Lab 8 的 Q&A
什么是 DbSet?
DbSet 简单地表示了可以从数据库中查询到的实体集合。当我们再次写一个 Linq 查询时,DbSet 对象会对查询进行内存转换,然后触发数据库。
在我们的例子中,「Employee」是一个 DbSet,它承载了所有可以从数据库中查询到的 Employee 实体对象。每一次我们尝试访问「Employees」时,它都将从“TblEmployee”表中获取记录,然后将其转换为「Employees」对象并返回集合。
数据库连接串和数据访问层是如何连接的?
Mapping 通过名称来实现。在我们的例子中,ConnectionString 名称和数据访问层类的名称是一样的,即「SalesERPDAL」,因此它们是自动映射的。
我们可以更改 ConnectionString 的名称吗?
答案是肯定的。在这个例子中,我们需要在数据访问层类中定义一个构造函数如下:
public SalesERPDAL():base("NewName") {}
3. 组织所有
我们需要做几个改变,使得所有是有组织和有意义的。
第一步:重命名
「TestController」换名为 「EmployeeController」。GetView 行为方法改为 Index。Test 文件夹(Views 文件夹下) 改为 Employee「MyView」视图改为「Index」。
第二步:从 EmployeeListViewModel 中删除 UserName 属性
第三步:从视图中删除 UserName
打开 View/Employee.Index.cshtml 视图,然后从中删除 UserName。
简单来说,就是删除如下代码:
Hello @Model.UserName
第四步:在 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」。
4. Lab 9 — 创建 Data Entry Screen
第一步:创建 Action 方法
在 EmployeeController 中创建一个 Action 方法,命名为「AddNew」,如下:
public ActionResult AddNew() { return View("CreateEmployee");}
第二步:创建 View
在文件夹 View/Employee 下创建一个 View,命名为「CreateEmployee」。代码如下:
@{ Layout = null;}
第三步:在 Index 视图中创建一个链接
打开 Index.cshtml,然后增加一个超链接指向 AddNew 行为的URL。
第四步:执行并测试应用
按下 F5 并执行应用。
Lab 9 的 Q&A
Form 标签的目的是什么?
Form 标签中的方法属性是什么?
它决定了请求的类型。请求也许是如下的其中一种:get、post、put 或者是 delete。
Get:当我们想获取什么数据时Post:当我们想创建什么数据时Put:当我们想更新什么数据时Delete:当我们想删除什么数据时
运用 Form 标签和通过浏览器地址栏或者超链接来发出请求,有何区别?
当我们使用 Form 标签来发送请求时,所有输入控件中的值都会伴随着请求一起被处理。
Checkbox、Radio 按钮和 Dropdowns 控件中的值也会被发送吗?
答案是肯定的。所有输入控件(输入类型为 Text,Radio,Checkbox)以及 Dropdowns(表示的是被选中的元素)都会被发送。
输入的值如何发送给服务器?
当请求的类型是 Get、Put 或者 Delete 时,输入的值会以查询字符串参数的方式发送。
当请求的类型是 Post 时,输入的值会以 Post 数据发送。
输入控件中的 Name 属性的目的是什么?
Name 和 Id 属性的目的是否是相同的?
「input type = submit」 和 「input type = button」有什么区别?
当我向服务器发送请求时,Submit 按钮会被特殊用到。而一个简单的按钮是用来处理一些客户端的行为的。简单的按钮不会自己做一些事情。
5. Lab 10 — 在服务器/Controller 获取 Posted 数据
第一步:创建 SaveEmployee 行为方法
在 Employee 控制器中创建一个行为方法,命名为 SaveEmployee,代码如下:
public string SaveEmployee(Employee e) { return e.FirstName + "|"+ e.LastName+"|"+e.Salary;}
第二步:执行并测试
按下 F5 并执行应用。
Lab 10 的 Q&A
在 Action 方法里,Textbox 的值是如何更新 Employee 对象的?
无论何时一个包含参数的请求向 Action 方法发送时,Model Binder 都会自动执行。Model Binder 将会遍历方法的所有原始参数,然后将它们与发送过来的数据的参数的名称相对比。(发送过来的数据意味着要么是 Posted 数据,或者是查询字符串)。当匹配成功时,会依照发送过来的数据分配给参数。当 Model Binder 遍历完每一个类参数中的每一个属性后,然后和发送过来的数据做对比。当匹配成功后,依照发送过来的数据分配给参数。
当两个参数是特指的,将会发生什么?例如第一个是「Employee」,第二个是「FirstName」?
FirstName 将会在初始的变量 FirstName 中更新,也会在 e.FirstName 属性中更新。
ModelBinder 可以和组合关系一起运用吗?
答案是肯定的。但是在这个情形下控件的名称应该被给出。例如:
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 如下:
......... .........
6. Lab 11 — 重置和取消按钮
第一步:开始重置和取消按钮
增加一个重置和取消按钮,代码如下:
.........
注意:保存和取消按钮都有相同的「Name」属性,即「BtnSubmit」。
第二步:定义 ResetForm 方法
在 HTML 顶部区域增加一个 Script 标签,用于创建一个 JavaScript 方法,命名为 ResetForm。代码如下:
改变 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();}
第四步:执行应用
第五步:测试重置功能
第六步:测试 Save 和 Cancel 功能
Lab 11 的 Q&A
为什么保存和取消按钮的名称是一样的?
Submit 按钮也是一个输入按钮。因为按钮的值也会被发送。
在 Action 方法中。Model Binder 将会处理这些工作。它将会依照输入的值(伴随着请求)更新参数的值。
实现多个提交按钮的方式是什么?
这里有多个方式。我介绍其中三种。
隐藏 Form 元素
第一步:在视图中创建一个隐藏 Form 元素
第二步:改变 Submit 按钮为一个常规按钮,并且通过 JavaScript 将上面的 Form 发送
运用 JavaScript 动态地改变动作 URL
Ajax
不再运用 Submit 按钮,取而代之的是简单的输入按钮,然后运用 JQuery 或者其它库来实现纯 Ajxa 请求。
为什么我们在实现重置功能时,没使用「input type = reset」?
「input type = reset」控件不会清除值,它只是将控件的值改为默认的值。例如:
在这个例子中,控件的默认值是「Sukesh」。
当名称没有与类中的属性名称匹配时,会怎样?
这是一个在面试中经常被问到的常规问题。
例如我们有一段 HTML 代码如下:
First Name:
Last Name:
Salary:
现在我们的 Model 类包含的属性名称有 FirstName,LastName 和 Salary。因此默认的 Model Binder 将不会在这里处理。
在这种情形下,我们有三种解决方案:
在 Action 方法内部,运用 Request.Form 语法来检索发送过来的值,然后手动构造 Model 对象如下:
public ActionResult SaveEmployee() { Employee e = new Employee(); e.FirstName = Request.Form["FName"]; e.LastName = Request.Form["LName"]; e.Salary = int.Parse(Request.Form["Salary"])......}
运用参数名称,然后手动创建 Model 对象如下:
public ActionResult SaveEmployee(string FName, string LName, int Salary) { Employee e = new Employee(); e.FirstName = FName; e.LastName = LName; e.Salary = Salary;......}
创建自定义的 Model Binder 来替换默认的 Model Binder。
第一步:创建自定义的 Model Binder
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; }}
第二步:用这个新的 Model Binder 来替换默认的 Model Binder
public ActionResult SaveEmployee([ModelBinder(typeof(MyEmployeeModelBinder))]Employee e, string BtnSubmit) { ......}
RedirectToFunction 函数是做什么的?
它用来产生 RedirectToRouteResult,就像 ViewResult 和 ContentResult一样(在第一天学习中探讨)。RedirectToRouteResult 是 ActionResult 的子类,它代表的是间接的响应。当浏览器接到 RedirectToRouteResult,它就会产生新的请求到一个新的行为方法。
注:这里浏览器对新的请求是有责任的,因此 URL 将会更新到一个新的 URL。
什么是 EmptyResult?
它是 ActionResult 的其中一个子类。当浏览器接到的响应是 EmptyResult 时,它将会简单地呈现一个空白屏幕。它简单地代表「No Result」。
在我们的例子中,这种情形不会发生。只要确保所有的代码路径返回的值。
注:当 Action 方法返回的值是空的,结果等同于 EmptyResult。
7. Lab 12 — 在数据库中保存记录并更新 Grid
第一步:在 EmployeeBusinessLayer 中创建 SaveEmployee
public Employee SaveEmployee(Employee e) { SalesERPDAL salesDal = new SalesERPDAL(); salesDal.Employees.Add(e); salesDal.SaveChanges(); return e;}
第二步:改变 SaveEmployee 行为方法
在 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();}
第三步:执行并测试
按下 F5 并执行应用。导航到 Data 入口屏幕并输入一些合法的值。
8. Lab 13 — 增加服务器端认证
在 Lab 10 中,我们已经了解了 Model Binder 的基本功能。现在我们来更多地了解下。
Model Binder 通过发送过来的数据来更新 Employee 对象。但是这个不是 Model Binder 的唯一功能。Model Binder 还更新 ModelState。它有一个属性,称为 IsValid,这个决定了 Model(即 Employee 对象)是否更新成功了。如果服务器端的认证失败了,Model 将不会更新。它承载了认证错误。例如:ModelState["FirstName"].Errors 包含了与 First Name 相关的错误。它承载了发送过来的数据值。(Posted 数据或者是查询字符串数据)
在 ASP.NET MVC 中,我们运用 DataAnnotations 来实现服务器端的认证。
在了解 Data Annotation 之前,我们先了解一些 Model Binder。
ModelBinder 如何处理初始数据类型?
当 Action 方法包含初始类型参数时,Model Binder 将会把参数的名称与发送过来的数据进行对比。(发送过来的数据是 Posted 数据或者是查询字符串)
当匹配成功时,将会依照发送过来的数据分配给参数。当匹配失败时,参数将会被分配给默认值。(对于整型的默认值是0,对于字符串的默认值是 null)当数据类型不匹配的异常被抛出时,这种情况下不会进行分配操作。
Model Binder 如何处理类?
当参数是一个类参数,Model Binder 将会遍历所有类的所有属性,并且将每一个属性名称与发送过来的数据做对比。
当匹配成功时,如果发送过来的数据是空的。
Null 值将会被分配给属性。如果不能分配,默认的值将会被设置,并且 ModelState.IsValid 将会被设置为 false。
当 Null 值可以分配给属性时,这将会被视作不合法的值,因为属性附上了认证,因此 ModelState.IsValid 将会被设置为 false。
当匹配成功时,发送过来的数据不为空。
当数据类型不匹配时,将不会分配值。或者服务器端的认证失败,将分配 Null 值。此时 ModelState.IsValid 将会被设置为 false。如果不能分配 Null 值,默认的值将会被设置。
当匹配不成功时,参数将会被分配为默认值。(对于整型的默认值是0,对于字符串的默认值是 null)。在这种情形下,ModelState.IsValid 将不会受到影响。
现在让我们了解一下如何向项目中增加认证功能。
第一步:运用 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; }......}
第二步:改变 SaveEmployee 行为方法。
打开 EmployeeController,改变 SaveEmloyee 行为方法如下:
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();}
第三步:在视图中呈现错误
改变「Views/Index/CreateEmployee.cshtml」中的代码如下。
这次我们将会运用「Table」标签来格式化一下 UI。
First Name: | |
@Html.ValidationMessage("FirstName") | |
Last Name: | |
@Html.ValidationMessage("LastName") | |
Salary: | |
@Html.ValidationMessage("Salary") | |
第四步:执行并测试
按下 F5 并执行应用。导航到「Employee/AddNew」行为方法,并测试应用。
Test 1
Test 2
注:你也许会遇到如下错误。
「The model backing the 'SalesERPDAL' context has changed since the database was created. Consider using Code First Migrations to update the database.」
Database.SetInitializer(new DropCreateDatabaseIfModelChanges
Database 类在命名空间 System.Data.Entity 中。
Lab 13 的 Q&A
@Html.ValidationMessage 是做什么事情的?
@ 意味着是 Razor 代码。Html 是 视图中的 HtmlHelper 类的实例。ValidationMessage 是 HtmlHelper 类的方法,用于呈现错误信息。
ValidationMessage 函数如何工作的?
ValidationMessage 是一个函数。它在运行时执行。就像我们之前所探讨的,ModelBinder 更新 ModelState。ValidationMessage 根据 Key 值来从 ModelState 中获取错误信息并呈现。
例如:ValidationMessage("FirstName") 呈现有关 First Name 的错误信息。
我们还有其它类似于 Required 和 StringLength 的属性吗?
答案是肯定的。如下所示:
DataType。确保数据是指定的类型,例如邮箱、信用卡号、URL 等。EnumDataTypeAttribute。确保在 Enumeration 中存在该值。Range Attribute。确保值在一个指定的区域内。Regular。认证值是否是指定的表达式。Required。确保值是存在的。StringLength。认证字符串中的最大和最小字符长度。
Salary 是如何认证的?
我们并没有向 Salary 属性添加 Data Annotation,但是它依然得到了认证。原因是这样的,在更新模型的时候,Model Binder 依然考虑到了属性的数据类型。
在 Test 1 中,我们保持 Salary 为一个空字符串。在这种情形下,因为我们有 Model Binder,ModelState.IsValid 将会为失败的并且 ModelState 将会承载与 Salary 相关的错误认证信息,这些信息将会通过 Html.ValidationMessage("Salary") 被显示在 View 中。
在 Test 2 中,Salary 数据类型匹配失败,因此认证也是失败的。
这意味着,默认情况下,整型属性将会被强制?
答案是肯定的。不仅仅是整型,所有的值类型都会被强制,因为它们不能为 Null 值。
如果我们想有一个非 Required 整型域该如何?
把它设置为 Nullable?
public int? Salary{get;set;}
如何特定为 Salary 改变认证信息?
默认情况下,认证是支持 Salary的(因为它是整数类型),不会允许我们改变认证信息。我们可以通过 Regular 表达式、Range 或者是 Custom Validator来同样达到这个目的。
为什么当认证失败时,值会被清空?
因为这是一个新的请求。数据入口视图将会在开始被呈现,并且在呈现之后和发展视图是一样的,但是和请求视图是不一样的。我们将会在第四天的学习中来学习如何保持值不变。
我们可以明确地让 Model Binder 来执行吗?
答案是肯定的。只需要简单地从 Action 方法中移走参数。默认情况下,它将会停止运行中的默认 Model Binder。
在这种情形下,我们可以运用 UpdateModel 函数如下:
Employee e = new Employee(); UpdateModel
注:UpdateModel 不会处理原始数据类型。
UpdateModel 方法 和 TryUpdateModel 方法有什么区别?
TryUpdateModel 和 UpdateModel 是一样的,除了一个附加的优势。
当 Model 由于任意原因适配失败时,UpdateModel 将会抛出异常。这种情况下,ModelState.IsValid 函数将不会有任何左右。
TryUpdateModel 是将 Employee 对象和函数参数保持精确地一致。如果更新失败了,ModelState.IsValid 将会为 False。
客户端的认证是如何的?
这个可以被手动完成,或者我们可以运用 HTML Helper 类。
我们将会在第四天的学习中探讨这两种手动的客户端认证方式,以及运用 HTML Helper 来自动客户端认证。
我们可以为一个属性附加上多个 DataAnnotation 吗?
答案是肯定的。在这种情形下,多个认证都会被触发。
9. 自定义服务器端认证
第一步:创建自定义认证
创建一个新的类,叫做 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 contain @"); } } return ValidationResult.Success; }}
第二步:向 First Name 中附加认证
打开 Employee 类,然后将 FirstName 属性的默认的「Required」属性移走,附加上 FirstNameValidation。
[FirstNameValidation]public string FirstName { get; set; }
第三步:执行并测试
按下 F5,导航到「Employee/AddNew」动作。
Test 1
Test 2
10. 总结
这里我们完成了第三天的学习。在第四天学习中,我们将会提升项目到一个新的版本。第四天的学习事项如下:
实现客户端的认证。理解 HTML Helper。实现 Authentication。部分视图增加 Footers。
发表评论
暂时没有评论,来抢沙发吧~