The DefaultModelBinder in ASP.NET MVC -ASP.NET MVCのデフォルトモデルバインディング


原文の住所これは私がCodeProjectの上で见た1篇の文章で、ここは私の翻訳の中国语版で、もしいかなる翻訳が适切でないところがあるならば、また各位が教えてあげることを惜しまないことを望みます!

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が1つ以上のFriendを持ってほしいからです.私はアメリカから来たので、AddressにStateを残すことにしました.PersonとAddressをデータベース内のデータテーブルと見なすことができますが、個人的には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でサンプル全体をダウンロードできます.