MVC學習筆記六:模型綁定【上】

模型綁定

這一章主要記錄一下MVC模型綁定

一.認識模型綁定

官方詳細介紹模型綁定的資料我沒找到,只是在MSDN上講DefaultModelBinder 類時介紹了一下將瀏覽器請求映射到數據對象。

這句話剛看上去不大明白意思,還是用自己的話總結一下:

模型綁定實際上是:

服務器端代碼利用用戶在表單中輸入的數據(或其它HTTP請求攜帶的數據),來構造動作方法所需要的參數對象的過程。數據的流向是從客戶端的HTML表單到服務器端動作方法。


更進一層的解釋是:
當我們在瀏覽器輸入一個地址即訪問一個動作時,動作調用器會負責在調用方法之前獲取該方法所需的所有參數。而默認動作調用器依賴於模型綁定來獲取動作方法中的參數值。其中每一個參數依賴於各自的模型綁定器,它們可能是用戶自定義的模型綁定器,也可能是默認的模型綁定器。

下面從一個簡單的示例開始演示一下默認模型綁定器。

二.使用默認模型綁定

1)綁定簡單類型

打開VS,新建一個空模板的MVC 3項目如下:


在Models文件夾下面新建一個Sheep類文件,如下:
namespace MvcModelBind.Models
{
    public class Sheep
    {
        private string _name = "No name!";

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
    }
}
在Controllers文件夾下添加一個控制器,如下:


在該控制器下新建一個方法,注意添加Sheep的引用:
        public ActionResult ModelBindSheep(Sheep sheep)
        {
            return View(sheep);
        }
F6編譯項目,爲上面的方法新建一個強類型視圖,如下:


將視圖代碼修改爲:
@model MvcModelBind.Models.Sheep

@{
    ViewBag.Title = "ModelBindSheep";
}

<h2>ModelBinding示例:綁定Sheep對象的Name屬性</h2>

@using (Html.BeginForm("ModelBindSheep", "Sheep"))
{
    
    @:小羊的名字:@Html.TextBoxFor(n => n.Name, new { style = "background-color:#FF83FA" })<br />

    @:目前小羊的名字是:@Model.Name <br />

    <input type="submit" name="btn" value="提交" />
}
運行查看網頁:注意將網址改成對應的:/控制器名/action名:

更改名字,點擊提交按鈕會看到名字隨之變化,這裏斷點進action方法裏看一下,具體的方法參數值:



實際上,上面的模型綁定流程大體上可以分爲如下:

1)當我們點擊提交按鈕時,模型綁定器發現頁面需要綁定一個Sheep類,而且這個類有一個string型的屬性Name需要賦值;

2)於是模型綁定器就要找能爲這個Name屬性賦值的數據源。它首先在表單中查找,結果發現TextBox的name值是“Name”,於是就認爲這個控件的值可以爲Sheep類        的Name屬性賦值;

3)於是模型綁定器就將textBox的value值取出來,轉化成能爲Sheep類的Name屬性賦值的類型(這裏轉化爲string),並將轉化後的值賦給Sheep類的Name屬性;

4)賦值完成後,模型綁定器就能構造出一個Sheep類的實例對象,該對象裏的屬性值都是剛剛從表單中取出綁定過來的。然後將這個實例對象送給動作調用器;

5)動作調用器在調用action方法之前,將收到的實例對象注入到動作方法的參數裏去。對應上面的示例,就是注入到參數sheep中。

經過上述幾部,纔有我們在斷點時監視到的參數sheep的Name屬性值隨TextBox值變化而變化的情形。

如果要說什麼是默認模型綁定,那麼上面的便是咯偷笑

注:上面演示的示例,模型綁定器是在表單中找到與參數名(Name)匹配的數據源的;實際上默認的模型綁定器不僅僅會從表單中查找,它查找數據源的順序是:

1.用戶在HTML表單form中提供的值;(即Request.Form)
2.從應用程序路由獲得的值;(即RouteData.Values)
3.URL中查詢字符串部分的值;(即Request.QueryString)
4.請求中的上傳文件。(即Request.Files)

以上,默認綁定器只要找到一個值,搜索便會停止。

2)綁定複合類型

a)綁定複合類型簡單示例

上面綁定的僅僅是一個string型的屬性Name,接下來綁定一個複雜類型AdditionalInformation AddInfo
其中AdditionalInformation 定義如下:
namespace MvcModelBind.Models
{
    public class AdditionalInformation
    {
        public string Country { get; set; }
        public string City { get; set; }
    }
}
在Sheep類中添加一個新的複合屬性AddInfo:
    public class Sheep
    {
        private string _name = "No name!";

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public AdditionalInformation AddInfo { get; set; }  
    }
更改ModelBindSheep方法對應的視圖代碼爲:
@model MvcModelBind.Models.Sheep

@{
    ViewBag.Title = "ModelBindSheep";
}

<h2>ModelBinding示例:綁定Sheep對象的Name屬性</h2>

@using (Html.BeginForm("ModelBindSheep", "Sheep"))
{
    
    @:小羊的名字:@Html.TextBoxFor(n => n.Name, new { style = "background-color:#FF83FA" })<br />

    @:目前小羊的名字是:@Model.Name <br />

    @:小羊所在國家: @Html.EditorFor(c=>c.AddInfo.Country) <br />

    @:小羊所在的城市: @Html.EditorFor(c=>c.AddInfo.City) <br />
    
    <input type="submit" name="btn" value="提交" />
}
運行查看網頁,對應的國家及城市Html源碼:
    小羊所在國家: <input class="text-box single-line" id="AddInfo_Country" name="AddInfo.Country" type="text" value="" /> <br />
    小羊所在的城市: <input class="text-box single-line" id="AddInfo_City" name="AddInfo.City" type="text" value="" /> <br />
可以看到name屬性是模型Sheep類的AddInfo屬性名+對應的AdditionalInformation類的Country/City屬性名組合而成。

實際上可以完全手動寫上面的HTML代碼,而不必借用Html輔助器,只不過用支持Lambda表達式的Html輔助器生成網頁源碼比較方便而已。

b)指定自定義前綴

前面所有的示例都有一個共同的特點就是:視圖中僅僅綁定了一個模型類,即一個Sheep類型的model
@model MvcModelBind.Models.Sheep
我在視圖代碼中增加另一個Sheep實例:
@using MvcModelBind.Models;
@model MvcModelBind.Models.Sheep

@{
    Sheep anotherSheep = new Sheep
    {
        Name = "Tom",
        AddInfo = new AdditionalInformation { Country = "America", City = "sanfrancisco" }
    };
}

@{
    ViewBag.Title = "ModelBindSheep";
}

<h2>ModelBinding示例:綁定Sheep對象的Name屬性</h2>

@using (Html.BeginForm("ModelBindSheep", "Sheep"))
{
    
    @:這是當前小羊的名字: @Html.EditorFor(a=>a.Name) <br />

    @:這是當前小羊的國家: @Html.EditorFor(a=>a.AddInfo.Country) <br />

    @:這是當前小羊的城市: @Html.EditorFor(a=>a.AddInfo.City)

    <p >--------------------------------------------------------</p>
    
    @:這是額外小羊的名字: @Html.EditorFor(a=>anotherSheep.Name) <br />

    @:這是額外小羊的國家: @Html.EditorFor(a=>anotherSheep.AddInfo.Country) <br />

    @:這是額外小羊的城市: @Html.EditorFor(a=>anotherSheep.AddInfo.City) <br />
    
    <input type="submit" name="btn" value="提交" />
}
編譯運行,無論我如何修改虛線下面的三個TextBox的值,點擊提交按鈕後這三個TextBox始終是顯示:

這說明虛線下面的三個根本就沒參與模型綁定。在對應的動作方法中添加一個參數:
        public ActionResult ModelBindSheep(Sheep sheep,Sheep sheep1)
        {
            return View(sheep);
        }
編譯運行,還是和上面一樣,無論我如何修改虛線下面的三個TextBox的值,點擊提交按鈕後這三個TextBox始終是恢復上圖的樣子。
其實模型綁定器在查找sheep1對應的參數值時,會去查找name爲sheep1.Name/AddInfo.Country/AddInfo.City的數據源,發現壓根就找不到,所以點擊提交按鈕後,虛線下面的三個TextBox又恢復原值。
看一下頁面這三個TextBox對應的name:
    <p >--------------------------------------------------------</p>
    這是額外小羊的名字: <input class="text-box single-line" id="anotherSheep_Name" name="anotherSheep.Name" type="text" value="Tom" /> <br />
    這是額外小羊的國家: <input class="text-box single-line" id="anotherSheep_AddInfo_Country" name="anotherSheep.AddInfo.Country" type="text" value="America" /> <br />
    這是額外小羊的城市: <input class="text-box single-line" id="anotherSheep_AddInfo_City" name="anotherSheep.AddInfo.City" type="text" value="sanfrancisco" /> <br />
可見,這些name都有一個前綴:anotherSheep,這是我在視圖中定義的額外Sheep類的實例名。
這樣一來,我就可以將動作方法中第二個參數名改成anotherSheep,來實現正確的模型綁定:
        public ActionResult ModelBindSheep(Sheep sheep, Sheep anotherSheep)
        {
            return View(sheep);
        }
這時候,編譯運行,修改這三個TextBox值,提交頁面,會發現值都會隨之變化了;
其實除了將方法的第二個參數名改成anotherSheep外,還有另一種方法,見下:
        public ActionResult ModelBindSheep(Sheep sheep,[Bind(Prefix="anotherSheep")] Sheep sheep1)
        {
            return View(sheep);
        }
該方式是利用Bind註解屬性來告訴模型綁定器找數據源時,應該找數據源name以什麼來開頭的。

c)綁定或不綁定某些屬性

假如我不想綁定Sheep中的Name屬性,只想綁定AddInfo屬性,但是我前臺頁面又有對應的Name數據源,因爲綁定器會自動找到並綁定它。

可以利用Bind註解屬性:
        public ActionResult ModelBindSheep([Bind(Include="AddInfo")] Sheep sheep)
        {
            return View(sheep);
        }
或:
        public ActionResult ModelBindSheep([Bind(Exclude="Name")] Sheep sheep)
        {
            return View(sheep);
        }
或者在類定義上使用:
    [Bind(Exclude = "Name")] 
    public class Sheep
    {
        private string _name = "No name!";

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public AdditionalInformation AddInfo { get; set; }  
    }
或:
    [Bind(Include = "AddInfo")] 
    public class Sheep
    {
        private string _name = "No name!";

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public AdditionalInformation AddInfo { get; set; }  
    }

但是一個需要注意的是:如果在類定義上使用了:
[Bind(Exclude = "Name")] 
又在動作方法參數上使用了:
[Bind(Include="Name")] 
那麼,這個Name屬性仍然是不會被綁定的!


3)綁定到集合

上面的示例均是綁定的單獨對象,即使使用額外對象也是屈指可數,實際應用中可能會要求綁定幾十到幾百個對象。
下面以一個簡單的示例介紹綁定到集合,將原先的視圖代碼修改爲:
@using MvcModelBind.Models;
@model List<MvcModelBind.Models.Sheep>


@{
    ViewBag.Title = "ModelBindSheep";
    int i = 0;
}

<h2>綁定到集合</h2>

@using (Html.BeginForm("ModelBindSheep", "Sheep"))
{ 
     for ( i = 0; i < Model.Count; i++)
    {
        @:第 @(i+1) 只羊的名字: @Html.EditorFor(n=>n[i].Name) <br />
        @:第 @(i+1) 只羊的國家: @Html.EditorFor(n=>n[i].AddInfo.Country) <br />
        @:第 @(i+1) 只羊的城市: @Html.EditorFor(n=>n[i].AddInfo.City) <br />
        <p>----------------------------------------------------</p>
    }
    
    <input type="submit" name="btn" value="提交" />
}

對應的方法修改爲:
        public ActionResult ModelBindSheep(List<Sheep> sheeps)
        {
            if (sheeps==null)
            {
                sheeps = new List<Sheep>()
                {
                    new Sheep { Name="Tom" , AddInfo=new AdditionalInformation{Country="China",City="ShangHai"}},
                    new Sheep { Name="Jack" , AddInfo=new AdditionalInformation{Country="America",City="sanfrancisco"}},
                    new Sheep { Name="Jerry" , AddInfo=new AdditionalInformation{Country="France",City="London"}},
                };
            }
            return View(sheeps);
        }
編譯運行:


對應表單中的源碼:
<form action="/Sheep/ModelBindSheep" method="post">        
	第 1 只羊的名字: <input class="text-box single-line" name="[0].Name" type="text" value="Tom" />
	<br />
        第 1 只羊的國家: <input class="text-box single-line" name="[0].AddInfo.Country" type="text" value="China" />
	<br />
        第 1 只羊的城市: <input class="text-box single-line" name="[0].AddInfo.City" type="text" value="ShangHai" />
	<br />
	<p>----------------------------------------------------</p>
        第 2 只羊的名字: <input class="text-box single-line" name="[1].Name" type="text" value="Jack" />
	<br />
        第 2 只羊的國家: <input class="text-box single-line" name="[1].AddInfo.Country" type="text" value="America" />
	<br />
        第 2 只羊的城市: <input class="text-box single-line" name="[1].AddInfo.City" type="text" value="sanfrancisco" />
	<br />
	<p>----------------------------------------------------</p>
        第 3 只羊的名字: <input class="text-box single-line" name="[2].Name" type="text" value="Jerry" />
	<br />
        第 3 只羊的國家: <input class="text-box single-line" name="[2].AddInfo.Country" type="text" value="France" />
	<br />
        第 3 只羊的城市: <input class="text-box single-line" name="[2].AddInfo.City" type="text" value="London" />
	<br />
	<p>----------------------------------------------------</p>
	<input type="submit" name="btn" value="提交" />
</form>
修改對應TextBox的值,提交表單後,可以看到值都隨之變化,說明綁定成功!

4)綁定非數字索引的集合

這裏藉助於隱藏Input元素,作爲指定數據的的鍵,只要將表單中的視圖代碼修改爲:

@using (Html.BeginForm("ModelBindSheep", "Sheep"))
{ 
    
    <input type="hidden" name="index" value="firstSheep" />
        @:第 @(i+1) 只羊的名字: @Html.Editor("[firstSheep].Name") <br />
        @:第 @(i+1) 只羊的國家: @Html.Editor("[firstSheep].AddInfo.Country") <br />
        @:第 @(++i) 只羊的城市: @Html.Editor("[firstSheep].AddInfo.City") <br />

        <p>----------------------------------------------------</p>
    <input type="hidden" name="index" value="secondSheep" />
        @:第 @(i+1) 只羊的名字: @Html.Editor("[secondSheep].Name") <br />
        @:第 @(i+1) 只羊的國家: @Html.Editor("[secondSheep].AddInfo.Country") <br />
        @:第 @(++i) 只羊的城市: @Html.Editor("[secondSheep].AddInfo.City") <br />

        <p>----------------------------------------------------</p>
    <input type="hidden" name="index" value="thirdSheep" />
        @:第 @(i+1) 只羊的名字: @Html.Editor("[thirdSheep].Name") <br />
        @:第 @(i+1) 只羊的國家: @Html.Editor("[thirdSheep].AddInfo.Country") <br />
        @:第 @(i+1) 只羊的城市: @Html.Editor("[thirdSheep].AddInfo.City") <br />
    
    <input type="submit" name="btn" value="提交" />
}
即可;

5)綁定到字典

同樣藉助於隱藏input元素,只不過元素的name屬性變成了字典的鍵,修改視圖代碼爲如下:
@using MvcModelBind.Models;
@model Dictionary<string,MvcModelBind.Models.Sheep>


@{
    ViewBag.Title = "ModelBindSheep";
    int i = 0;
}

<h2>綁定到字典</h2>

@using (Html.BeginForm("ModelBindSheep", "Sheep"))
{ 
    
    <input type="hidden" name="[0].key" value="firstSheep" />
        @:第 @(i+1) 只羊的名字: @Html.Editor("[0].value.Name") <br />
        @:第 @(i+1) 只羊的國家: @Html.Editor("[0].value.AddInfo.Country") <br />
        @:第 @(++i) 只羊的城市: @Html.Editor("[0].value.AddInfo.City") 

        <p>----------------------------------------------------</p>
    <input type="hidden" name="[1].key" value="secondSheep" />
        @:第 @(i+1) 只羊的名字: @Html.Editor("[1].value.Name") <br />
        @:第 @(i+1) 只羊的國家: @Html.Editor("[1].value.AddInfo.Country") <br />
        @:第 @(++i) 只羊的城市: @Html.Editor("[1].value.AddInfo.City") 

        <p>----------------------------------------------------</p>
    <input type="hidden" name="[2].key" value="thirdSheep" />
        @:第 @(i+1) 只羊的名字: @Html.Editor("[2].value.Name") <br />
        @:第 @(i+1) 只羊的國家: @Html.Editor("[2].value.AddInfo.Country") <br />
        @:第 @(++i) 只羊的城市: @Html.Editor("[2].value.AddInfo.City") <br />

    <input type="submit" name="btn" value="提交" />
}

對應的方法修改爲:
        public ActionResult ModelBindSheep(Dictionary<string,Sheep> sheeps)
        {
            if (sheeps == null)
            {
                sheeps = new Dictionary<string, Sheep>();
                sheeps.Add("firstSheep", new Sheep { Name="Tom" , AddInfo=new AdditionalInformation{Country="China",City="ShangHai"}});
                sheeps.Add("secondSheep", new Sheep { Name = "Jack", AddInfo = new AdditionalInformation { Country = "America", City = "sanfrancisco" }});
                sheeps.Add("thirdSheep", new Sheep { Name = "Jerry", AddInfo = new AdditionalInformation { Country = "France", City = "London" }});
            }
            return View(sheeps);
        }
編譯運行,修改TextBox的值,點擊提交按鈕,同樣是可以實現綁定的!



發佈了49 篇原創文章 · 獲贊 71 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章