The DefaultModelBinder in ASP.NET MVC -ASP.NET MVC的默认模型绑定器

原文地址
这是我在CodeProject上面看到的一篇文章,这里是我翻译的中文版,如果有任何翻译不恰当之处,还望各位不吝赐教!

Models

  在开始之前,我想花一点时间阐述一下我们将要在C#中绑定的模型(Models)。我们来创建一个包含Friend和Address的Person模型。

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Address
{
    public int Id { get; set; }
    public string City { get; set; }
    public string State { get; set; }
}

public class PersonViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Person Friend { get; set; }
    public Address[] Addresses { get; set; }
}

  这种设定有点不合理,因为我希望一个Person拥有不止一个Friend。由于我来自美国,我决定在Address里面保留State
  你可以将PersonAddress看做是数据库里面的数据表,我个人推荐在ViewModels里面使用Model的强类型视图。我比较喜欢将ViewModels看做是数据的汇总。
  下面是Controller的详文: 

public class PersonController : Controller
{
    public ActionResult Edit()
    {
        var vm = new PersonViewModel
        {
            Id = 1,
            Name = "Jane Doe",
            Friend = new Person
            {
                Id = 2,
                Name = "Jon Doe"
            },
            Addresses = new Address[]
            {
                new Address
                {
                    Id = 1,
                    City = "Athens",
                    State = "Texas"
                },
                new Address
                {
                    Id = 2,
                    City = "Paris",
                    State = "Texas"
                }
            }
        };
        return View(vm);
    }

    [HttpPost]
    public ActionResult Edit(PersonViewModel vm)
    {
        return View("Details", vm);
    }
}

  你可能会奇怪,在德克萨斯州究竟有没有雅典和巴黎。
  你可以开始想象默认的绑定器如何处理整个ViewModel,我写了少量的代码来处理大量的数据。

绑定到复杂数据类型

  你可以把模型绑定器看做一个将HTTP文本信息转换成C#对象的机器,为了绑定复杂C#类型,我们在Razor视图里面所需要就是下面的代码:
  

@using (Html.BeginForm("Edit", "Person"))
{
    <div>
        @Html.HiddenFor(m => m.Id)
        <label>Name:</label>
        <span>@Html.TextBoxFor(m => m.Name)</span>
    </div>
    <div>
        <button>Submit</button>
    </div>
}

  所需要的HTML非常简单: 

<form action="/" method="post">
    <div>
        <input id="Id" name="Id" type="hidden" value="1" />
        <label>Name:</label>
        <span>
            <input id="Name" name="Name" type="text" value="Jane Doe" />
        </span>
    </div>
    <div>
        <button>Submit</button>
    </div>
</form>

  默认的绑定器从input标签里面获得name特性的值,绑定到C#对象属性。Value属性设置初始值。下面就是当我点击“Submit”按钮的时候,Html请求体的内容。

Id=1&Name=Jane+Doe

  我希望你将HTTP请求看做一些键值对,因此,name指向C#中的name属性,value指向它的值,这和下述的Controller中的代码完成了同样的工作。  

var vm = new PersonViewModel();
vm.Id = 1;
vm.Name = "Jane Doe";

  让人吃惊的是,默认的绑定器如何将C#中的强类型考虑进来的。String类型获得string值,integer类型获得integer值。
  

绑定到内嵌类型

  让我们将目光放在内嵌类型上面,我们所需要的代码如下: 

<div>
    <label>Friend:</label>
    @Html.HiddenFor(m => m.Friend.Id)
    <span>@Html.TextBoxFor(m => m.Friend.Name)</span>
</div>

  这看起来非常复杂,但是,默认绑定器所看到的全部就是Friend.Name。因此,它会对所有C#对象进行递归遍历,寻找对应的属性。整个引擎自带递归功能,所以你想走多深就能走多深,这可以让复杂的数据集被轻松使用。
  如果你回顾Controller,你会发现PersonViewModel vm被作为了Edit()的参数。默认的模型绑定器在绑定到ViewModel的时候会获取整个对象。

new PersonViewModel
{
    Friend = new Person
    {
        Id = 2,
        Name = "Jon Doe"
    }
};

  它从下面的HTML中获取所有的信息:

<div>
    <label>Friend:</label>
    <input id="Friend_Id" name="Friend.Id" type="hidden" value="2" />
    <span>
        <input id="Friend_Name"
            name="Friend.Name"
            type="text"
            value="Jon Doe" />
    </span>
</div>

  只要你将name映射到正确的对象属性上,默认的模型绑定器将会处理剩下的工作。

绑定列表

  默认的绑定器需要一个跟踪对象列表的方法,按照惯例,它使用了一个类似于数组的语法来完成这个事情。Look:  

for (var i = 0; i < Model.Addresses.Count(); i++)
{
    <div>
        @Html.HiddenFor(m => m.Addresses[i].Id)
        <label>City:</label>
        <span>@Html.TextBoxFor(m => m.Addresses[i].City)</span>
        <label>State:</label>
        <span>@Html.TextBoxFor(m => m.Addresses[i].State)</span>
    </div>
}

  这是HTML部分 

<div>
    <input id="Addresses_0__Id"
        name="Addresses[0].Id"
        type="hidden" value="1" />
    <label>City:</label>
    <span>
        <input id="Addresses_0__City"
            name="Addresses[0].City"
            type="text"
            value="Athens" />
    </span>
    <label>State:</label>
    <span>
        <input id="Addresses_0__State"
            name="Addresses[0].State"
            type="text"
            value="Texas" />
    </span>
</div>
<div>
    <input id="Addresses_1__Id"
        name="Addresses[1].Id"
        type="hidden" value="2" />
    <label>City:</label>
    <span>
        <input id="Addresses_1__City"
            name="Addresses[1].City"
            type="text"
            value="Paris" />
    </span>
    <label>State:</label>
    <span>
        <input id="Addresses_1__State"
            name="Addresses[1].State"
            type="text"
            value="Texas" />
    </span>
</div>

  从个人角度来说,我并不喜欢这种古怪的语法。如果你回顾ViewModel,你也许已经注意到了,我使用了Address[]数组来完成了这魔法般的工作。默认的模型绑定器需要对索引敏感。因此,只要你从0索引开始,后续索引连续递增,它才会工作,一定要确定你的索引之间没有间隔。
  

总结

  现在到终场演出的时候了!让我们来看看默认模型绑定器如何工作。
这里写图片描述
  下面是HTTP请求内容: 

Id=1&Name=Jane+Doe&Friend.Id=2&Friend.Name=Jon+Doe&Addresses%5B0%5D.Id=1&
Addresses%5B0%5D.City=Athens&Addresses%5B0%5D.State=Texas&
Addresses%5B1%5D.Id=2&Addresses%5B1%5D.City=Paris&Addresses%5B1%5D.State=Texas

  现在你也许可以回头看看我在Controller中穿件的C#对象,去感受一下上面这一堆文本信息。尽情感受默认模型绑定器的美丽吧,美到你哭。
  如果你感兴趣的话,你可以在GitHub上下载整个样例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值