7 天玩轉 ASP.NET MVC — 第 3 天

目錄

0. 前言

咱們假定你在開始學習時已經閱讀了前兩天的學習內容。在第 2 天咱們完成了關於顯示 Employees 列表的項目。html

在第三天,咱們將會經過介紹數據訪問層和數據入口將它升級到一個新的層次。面試

1. 數據訪問層

在真實場景的項目中,若是沒有 Database,那麼這個項目是未完成的。在咱們的項目中,咱們尚未談到數據庫。第三天的首個 Lab 將會學習數據庫和數據庫層。數據庫

這裏咱們將使用 SQL Server 和 Entity Framework 來建立 Database 和 Database 訪問層。編程

簡單來講,什麼是 Entity Framework?

這是一個 ORM 工具。ORM 表明的是 Object Relational Mapping。即:對象關係映射。瀏覽器

在 RDBMS 領域中,咱們所談論的 Tables 表格和 Columns 列的這些方面,在 .NET 領域(面向對象領域)中被稱爲 Classes 類,對象和屬性。服務器

當咱們思考任何有關數據驅動應用的方式時,均可以得出如下兩種方式:mvc

  • 書寫代碼來和數據庫打交道(被稱爲數據訪問層和數據庫邏輯)app

  • 書寫代碼來將數據庫數據映射到面向對象中,反之亦然。ide

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」。

7 天玩轉 ASP.NET MVC — 第 3 天

第二步:建立 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,而後點擊安裝。

7 天玩轉 ASP.NET MVC — 第 3 天

第四步:建立數據訪問層

  • 在根目錄下建立一個新文件夾,命名爲「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,並執行應用。

7 天玩轉 ASP.NET MVC — 第 3 天

如今的數據庫中,咱們沒有任何的 Employees,因此咱們看見的是一個空白的 Grid。

查看數據庫,如今咱們能夠在 TblEmployee 表中看到全部的列。

7 天玩轉 ASP.NET MVC — 第 3 天

第十步:插入測試數據

向 TblEmployee 表中插入一些測試數據。

7 天玩轉 ASP.NET MVC — 第 3 天

第十一步:執行並測試應用

按下 F5 並再次運行應用。

7 天玩轉 ASP.NET MVC — 第 3 天

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()
{
	&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
}

如今執行的 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 並執行應用。

7 天玩轉 ASP.NET MVC — 第 3 天

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 並執行應用。

7 天玩轉 ASP.NET MVC — 第 3 天

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&rdquo; 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 屏幕。

第五步:測試重置功能

7 天玩轉 ASP.NET MVC — 第 3 天

第六步:測試 Save 和 Cancel 功能

7 天玩轉 ASP.NET MVC — 第 3 天

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

    <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>

  • 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 入口屏幕並輸入一些合法的值。

7 天玩轉 ASP.NET MVC — 第 3 天

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

7 天玩轉 ASP.NET MVC — 第 3 天

  • Test 2

7 天玩轉 ASP.NET MVC — 第 3 天

注:你也許會遇到以下錯誤。

「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

7 天玩轉 ASP.NET MVC — 第 3 天

  • Test 2

7 天玩轉 ASP.NET MVC — 第 3 天

10. 總結

這裏咱們完成了第三天的學習。在第四天學習中,咱們將會提高項目到一個新的版本。第四天的學習事項以下:

  • 實現客戶端的認證。

  • 理解 HTML Helper。

  • 實現 Authentication。

  • 部分視圖增長 Footers。

原文地址:Learn MVC Project in 7 days

本文系 OneAPM 工程師編譯整理。OneAPM 是應用性能管理領域的新興領軍企業,能幫助企業用戶和開發者輕鬆實現:緩慢的程序代碼和 SQL 語句的實時抓取。想閱讀更多技術文章,請訪問 OneAPM 官方博客

相關文章
相關標籤/搜索