7 天玩转 ASP.NET MVC — 第 3 天
目錄
- 第 1 天
- 第 2 天
- 第 3 天
- 第 4 天
- 第 5 天
- 第 6 天
- 第 7 天
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 中,我們可以使用如下三種的任意方法:
Database First 方法。創建一個有表,列和關系的數據庫。Entity Framework 將會生成對應的 Model 類(業務實體)和數據訪問層代碼。
Model First 方法。在這個方法中,Model 類和它們之間的聯系將會被 Model 設計者在 Visual Studio 中被手動定義。然后 Entity Framework 會自動創建數據訪問層和擁有表、列以及關系的數據庫。
Code First 方法。在這個方法中,手動創建 POCO 類。這些類中的關系將會被代碼所定義。當應用第一次執行時,Entity Framework 將會自動在數據庫服務器上創建數據訪問層和擁有表、列以及關系的數據庫。
什么是 POCO 類?
POCO 代表的是「Plain Old CLR Objects」。POCO 類代表的是我們所創建的簡單 .NET 類。在我們之前的例子中, Employee 類是一個簡單的 POCO。
2. Lab 8 — 向項目中添加數據訪問層
第一步:創建數據庫
連接 SQL Server 然后創建一個新的數據庫,命名為「SalesERPDB」。
第二步:創建 ConnectionString
打開 Web.config 文件,然后在 Configuration 區域內添加如下片段:
<connectionStrings> <add connectionString="Data Source=(local);Initial Catalog=SalesERPDB;Integrated Security=True"name="SalesERPDAL" providerName="System.Data.SqlClient"/> </connectionStrings>第三步:添加 Entity Framework 引用
右擊項目-> 管理 Nuget 包。搜索 Entity Framework,然后點擊安裝。
第四步:創建數據訪問層
在根目錄下創建一個新文件夾,命名為「DataAccessLayer」,然后在里面創建一個新的類,命名為「SalesERPDAL」。
在類頂部寫引用聲明如下
using System.Data.Entity;-
繼承 DbContext 的類「SalesERPDAL」
public class SalesERPDAL: DbContext
{
}
第五步:為 Employee 類創建主鍵
打開 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; } }第六步:定義映射
在「SalesERPDAL」類中添加如下聲明語句:
using WebApplication1.Models;在 SalesERPDAL 類中重寫 OnModelCreating 方法。
protected override void OnModelCreating(DbModelBuilder modelBuilder) {modelBuilder.Entity<employee>().ToTable("TblEmployee");base.OnModelCreating(modelBuilder); }注意:上述代碼中的片段「TblEmployee」代表的是表名。在運行時講自動被創建。
第七步:在數據庫中創建 Employees 屬性
在「SalesERPDAL」類中創建一個新屬性,命名為 Employee,如下所示:
public DbSet<employee> Employees{get;set;}DbSet 將會展示所有可以在數據庫中查詢到的 Employees。
第八步:改變業務層代碼,從數據庫中讀取數據
打開 EmployeeBusinessLayer 類,在頂部加上聲明如下:
using WebApplication1.DataAccessLayer;現在改變 GetEmployees 方法如下:
public List<employee> GetEmployees() {SalesERPDAL salesDal = new SalesERPDAL();return salesDal.Employees.ToList(); }第九步:執行并測試
按下 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 <hr />第四步:在 EmployeeController 中更改 Index 行為方法
更改 EmployeeController 中的 Index 行為方法如下:
public ActionResult Index() {………………employeeListViewModel.Employees = empViewModels;//employeeListViewModel.UserName = "Admin";-->Remove this line -->Change1return 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; } <!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>第三步:在 Index 視圖中創建一個鏈接
打開 Index.cshtml,然后增加一個超鏈接指向 AddNew 行為的URL。
<ahref="/Employee/AddNew">Add New</a>第四步:執行并測試應用
按下 F5 并執行應用。
Lab 9 的 Q&A
Form 標簽的目的是什么?
在第一天的系列學習中,我們已經明白了「Web 世界不會遵循事件驅動編程模型。它遵循的是請求響應模型。終端用戶發出請求,然后服務器給出響應。」Form 標簽是 HTML 中做出響應的其中一種方式。只要標簽里的提交按鈕被點擊,一個請求就將發送給動作屬性中指定的 URL 中。
Form 標簽中的方法屬性是什么?
它決定了請求的類型。請求也許是如下的其中一種:get、post、put 或者是 delete。
Get:當我們想獲取什么數據時
Post:當我們想創建什么數據時
Put:當我們想更新什么數據時
Delete:當我們想刪除什么數據時
運用 Form 標簽和通過瀏覽器地址欄或者超鏈接來發出請求,有何區別?
當我們使用 Form 標簽來發送請求時,所有輸入控件中的值都會伴隨著請求一起被處理。
Checkbox、Radio 按鈕和 Dropdowns 控件中的值也會被發送嗎?
答案是肯定的。所有輸入控件(輸入類型為 Text,Radio,Checkbox)以及 Dropdowns(表示的是被選中的元素)都會被發送。
輸入的值如何發送給服務器?
當請求的類型是 Get、Put 或者 Delete 時,輸入的值會以查詢字符串參數的方式發送。
當請求的類型是 Post 時,輸入的值會以 Post 數據發送。
輸入控件中的 Name 屬性的目的是什么?
就像我們之前所談論的,當按鈕被點擊時,輸入控件中的值將會隨著請求一起被發送。這會使得服務器在這個時刻接收到多于一個的值。為了在發送值的時候,單獨區別每一個值,就會為它們附加上一個 Key,這個 Key 就是「Name」屬性。
Name 和 Id 屬性的目的是否是相同的?
答案是否定的。就像剛才的問題所說,當請求發生時,「Name」屬性被 HTML 所使用,而「Id」屬性被開發者所使用,為一些 JavaScript 實現一些動態功能。
「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 對象的?
在 ASP.NET MVC 中,存有一個概念,叫做 Model Binder。
無論何時一個包含參數的請求向 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 如下:
... ... ... <input type="text" name="FName"> <input type="text" name="address.CityName"> <input type="text" name="address.StateName"> ... ... ...6. Lab 11 — 重置和取消按鈕
第一步:開始重置和取消按鈕
增加一個重置和取消按鈕,代碼如下:
... ... ... <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」。
第二步:定義 ResetForm 方法
在 HTML 頂部區域增加一個 Script 標簽,用于創建一個 JavaScript 方法,命名為 ResetForm。代碼如下:
<script>function ResetForm() {document.getElementById('TxtFName').value = "";document.getElementById('TxtLName').value = "";document.getElementById('TxtSalary').value = "";} </script>第三步:在 EmployeeController 的 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(); }第四步:執行應用
按下 F5 并執行應用。通過點擊“Add New”鏈接導航到 AddNew 屏幕。
第五步:測試重置功能
第六步:測試 Save 和 Cancel 功能
Lab 11 的 Q&A
為什么保存和取消按鈕的名稱是一樣的?
我們知道,一旦提交按鈕被點擊,一個請求就會被發送到服務器端。并且伴隨著請求,所有輸入控件的值也一起被發送。
Submit 按鈕也是一個輸入按鈕。因為按鈕的值也會被發送。
當保存按鈕被點擊時,保存按鈕的值,即「Save Employee」將會隨著請求一起被發送。當取消按鈕被點擊時,取消按鈕的值,即「Cancel」將會隨著請求一起被發送。
在 Action 方法中。Model Binder 將會處理這些工作。它將會依照輸入的值(伴隨著請求)更新參數的值。
實現多個提交按鈕的方式是什么?
這里有多個方式。我介紹其中三種。
- 隱藏 Form 元素
第一步:在視圖中創建一個隱藏 Form 元素
<form action="/Employee/CancelSave" id="CancelForm" method="get" style="display:none"> </form>第二步:改變 Submit 按鈕為一個常規按鈕,并且通過 JavaScript 將上面的 Form 發送
<input type="button" name="BtnSubmit" value="Cancel" onclick="document.getElementById('CancelForm').submit()" />-
運用 JavaScript 動態地改變動作 URL
... ... <input type="submit" name="BtnSubmit" value="Save Employee" οnclick="document.getElementById('EmployeeForm').action = '/Employee/SaveEmployee'" /> ... <input type="submit" name="BtnSubmit" value="Cancel" οnclick="document.getElementById('EmployeeForm').action = '/Employee/CancelSave'" /> Ajax
不再運用 Submit 按鈕,取而代之的是簡單的輸入按鈕,然后運用 JQuery 或者其它庫來實現純 Ajxa 請求。
為什么我們在實現重置功能時,沒使用「input type = reset」?
「input type = reset」控件不會清除值,它只是將控件的值改為默認的值。例如:
<input type="text" name="FName" value="Sukesh">在這個例子中,控件的默認值是「Sukesh」。
如果我們運用「input type = reset」來實現重置功能,那么每一次點擊重置按鈕,默認的值「Sukesh」將會被設置到 Textbox 中。
當名稱沒有與類中的屬性名稱匹配時,會怎樣?
這是一個在面試中經常被問到的常規問題。
例如我們有一段 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 />現在我們的 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(); }注:正如你所看見的,當 SaveEmployee 按鈕點擊后, ModelState.IsValid 失敗,ViewResult 指向「CreateEmloyee」視圖。
第三步:在視圖中呈現錯誤
改變「Views/Index/CreateEmployee.cshtml」中的代碼如下。
這次我們將會運用「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>第四步:執行并測試
按下 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.」
為了解決這個錯誤,僅僅在 Global.asax 文件中的 Application_Start 中添加如下聲明:
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<SalesERPDAL>());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<employee>(e);注: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。
原文地址:Learn MVC Project in 7 days
本文由OneAPM工程師編譯 ,想技術文章,請訪問OneAPM官方技術博客。
總結
以上是生活随笔為你收集整理的7 天玩转 ASP.NET MVC — 第 3 天的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDOJ 题目分类
- 下一篇: WPF下如何去除WebBrowser的滚