Model Binding 資料模型繫結
Model Binding
Model Binding 是 ASP.NET MVC 內建強大的功能之一。它幫助開發者省去許多時間。
它主要的目的就是協助您從 Http Request 中取得資料,省去手動把 Request 的資訊一個一個對應轉成物件。
它可以處理所有 Request 資料,包含 POST 和 GET 事實上它會取得所有的 Query String,POST,JSON 格式的資料並且將資料對應到物件本身,將資料填入對應的屬性(Property)。
簡單說它就是自動將 URL 傳來的參數值對應到 Action Method 的參數。預設 ASP.NET MVC 收到HTTP請求 (HTTP Request)後,就會啟動這個機制。
當然您可能要遵循一些規定的格式。
沒有模型繫結(Model Binding)時
在沒有模型繫結的時候我們要從 HTML Form 取得資料並轉換成 C# 中的一個物件
您需要撰寫的程式碼可能如下:
public ActionResult Create()
{
var product = new User(){
Birthday = DateTime.Parse(Request["birthdate"]),
UserId = Int32.Parse(Request["userid"]),
Name = Rrequest["name"]
};
}
如您所見,我們直接透過 Request 物件從 Http Request 取得每一個傳入的參數然後自己手動設定參數名稱和物件的屬性的對應關係。
Model Binding 簡單格式資料繫結
ASP.NET MVC 架構讓您可以使用 Model Binding 自動解析每一個參數的資料型別並對應到 Method 的參數。例如您的 Method 有兩個參數一個是整數 int 一個是字串 string 。如果模型繫結檢查出一個 Request 參數名稱跟 Action 中的參數名稱相同(不區分大小寫),它就會自動在收到請求之後映射和解析資料型別。
如果 Action 的參數並沒有對應傳入的資料,那它會自動帶入 null 當成預設值。
public ActionResult Create(DateTime birthdate, int userid, string name)
{
var product = new User(){
Birthday = birthdate,
UserId = userid,
Name = name
};
}
這看起來還不錯,事實上 MVC 的模型繫結機制甚至可以幫您對應您自訂的 User 物件,這就是複雜型物件繫結。
複雜型物件繫結
複雜型物件繫結會檢測屬性是不是跟表單中 input 的 name 名稱一樣,複雜型物件如果還包含另一個物件則可能需要使用 JSON 物件來傳遞資料。如果每一個 JSON 變數和 C# 物件的 Property 一樣,那 .NET 會對應 JSON 物件給 C# 物件。
public ActionResult Create(User user)
{
// User user here
}
我們只需要思考 Method 定義了那些參數,這個例子是 User 這個物件參數,然後 input 的 name 名稱是不是跟資料模型(User)的屬性名稱相同。
而且 ASP.NET MVC 框架(Framework)還提供了 Helper 來產生表單。
假設我們使用 Razor 樣版來繫結 User 模型。Html 擴充方法會自動映射資料模型的屬性,然後產生 HTML 如下:
@Html.TextBoxFor(model => model.UserId)
將會產生
<input name="UserId" value="" />
當使用者輸入資料,假設是 123 然後 submit 這個表單,這些資料將會隨著 http request 一起發送,這些資料會被包含在 http header 的 http post 資料之中。格式看起來像是 UserId=123。如果您有很多參數則每一個參數會用 & 符號分開。
ASP.NET MVC 會收到這些資料,確認型別。然後判斷 Action 參數中的這個物件(User)所有的公開屬性 public property。
比對符合的名稱之後將資料填入,在我們這個範例它會找到 User.UesrId。因為它是一個整數格式,然後會試著解析並將這個 string 轉型。
註: 您需要有一個空的建構子讓 Model Binding 能夠實例化這個物件。
MVC Model Binding 範例
讓我們示範一個簡單模型的例子 Customer
public class BaseEntity
{
public int Id{get;set;}
}
public class Customer:BaseEntity
{
public string FirstName{get;set;}
public strign LastName{get;set;}
}
為了能夠建立一個新的客戶( Customer 物件),View 需要一些欄位且 name 必須要和物件的屬性名稱相同。
@using(Html.BeginForm())
{
@Html.ValidationSummary(true)
<fieldset>
<legend>Customer</legend>
<div class="editor-label">
@Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.LastName)
@Html.ValidationMessageFor(model => model.LastName)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
這些 Html 擴充方法會產生對應的 HTML ,
當使用者點擊 "Create" 按鈕,伺服器就會收到 FirstName=Andy&LastName=You,然後我們就能儲存新的客戶資料。
[HttpPost]
public ActionResult Create(Customer customer)
{
if(ModelState.IsValid)
{
db.Customers.Add(customer);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.Id = new SelectList(db.Linceses, "Id", "Name", customer.Id);
return View(customer);
}
我們也可以單純的使用簡單型別
[HttpPost]
public ActionResutl Create(string firstName, string lastName)
{
var customer = new Customer{FirstName = firstName, LastName = lastName};
if(ModelState.IsValid)
{
db.Customers.Add(customer);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.Id = new SelectList(db.Licenses, "Id", "Name", customer.Id);
return View(customer);
}
或者
[HttpPost]
public ActionResult Create(FormCollection inputs)
{
var customer = new Customer{FirstName = inputs["FirstName"], LastName = inputs["LastName"]};
if(ModelState.IsValid)
{
db.Customers.Add(customer);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.Id = new SelectList(db.Licenses, "Id", "Name", customer.Id);
return View(customer);
}
這三個方法都正確,但第一個方法充分使用了 MVC 提供的自動繫結功能。
複雜型物件
複雜物件指的就是物件的屬性是另一個物件或者說類別的 Property 中包含另一個類別,
假設我們改變 Customer 類別讓他有一個 1 對 1 關係的 Avatar 類別。
public class Customer:BaseEntity
{
public string FirstName{get;set;}
public string LastName{get;set;}
public Avatar Avatar{get;set;}
}
public class Avatar:BaseEntity
{
public string Name {get;set;}
}
接著我們就要修改 View 讓它可以取得 Avatar 的 name 然後當表單送到 Controller 的時候儲存它。
這邊我們只需修改 View
@using(Html.BeginForm())
{
@Html.ValidationSummary(true)
<fieldset>
<legend>Customer</legend>
<div class="editor-label">
@Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.LastName)
@Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Avatar.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Avatar.Name)
@Html.ValidationMessageFor(model => model.Avatar.Name)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
然後會自動產生
<form action="/Customer/Create" method="post"> <fieldset>
<legend>Customer</legend>
<div class="editor-label">
<label for="FirstName">FirstName</label>
</div>
<div class="editor-field">
<input class="text-box single-line" id="FirstName" name="FirstName" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="FirstName" data-valmsg-replace="true"></span>
</div>
<div class="editor-label">
<label for="LastName">LastName</label>
</div>
<div class="editor-field">
<input class="text-box single-line" id="LastName" name="LastName" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="LastName" data-valmsg-replace="true"></span>
</div>
<div class="editor-label">
<label for="Avatar_Name">Name</label>
</div>
<div class="editor-field">
<input class="text-box single-line" id="Avatar_Name" name="Avatar.Name" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="Avatar.Name" data-valmsg-replace="true"></span>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
</form>
如您所見到的 的 name 會使用一個點 "." 來命名,這就是模型繫結計算出資料屬於 Customer 的 Avatar 的 Name 的方法。
另外如果您學習使用 ASP.NET MVC 4 的範例.要注意當您使用了複雜型別的資料模型在編輯的時候要記得設定 State 如下
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Product product)
{
if (ModelState.IsValid)
{
db.Entry(product).State = EntityState.Modified;
db.Entry(product.Category).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(product);
}