ASP.NET MVC - コレクションのコレクションにバインドする

できるかな?できるよね?と思って試したらやっぱりできたのでメモ。

コレクションにバインドする

まずはおさらいということでコレクションにバインドするサンプルです。 name属性に配列のようにインデックスを指定します。

ビュー

@using (Html.BeginForm()) {
    <input name="values[0]" type="text" value="a" />
    <input name="values[1]" type="text" value="b" />
    <input name="values[2]" type="text" value="c" />
    <button type="submit">Save</button>
}

コントローラ

public class SampleController : Controller {
    [HttpPost]
    public ActionResult Edit(IEnumerable<string> values) {
        Debug.WriteLine("[" + string.Join(", ", values) + "]");
        // [a, b, c]

        return RedirectToAction(null);
    }
}

今まで知らなかったのですが、次のようにname属性にインデックスを使わなくてもうまくバインドできるようです。(順番が大丈夫かちょっと不安・・・になるまでもないのかしら?)

ビュー

@using (Html.BeginForm()) {
    <input name="values" type="text" value="a" />
    <input name="values" type="text" value="b" />
    <input name="values" type="text" value="c" />
    <button type="submit">Save</button>
}

コレクションのコレクションにバインドする

配列の配列のようにインデックスを指定します。

ビュー

@using (Html.BeginForm()) {
    <input name="collectionOfValues[0][0]" type="text" value="a" />
    <input name="collectionOfValues[0][1]" type="text" value="b" />
    <input name="collectionOfValues[0][2]" type="text" value="c" />
    <input name="collectionOfValues[1][0]" type="text" value="d" />
    <input name="collectionOfValues[1][1]" type="text" value="e" />
    <input name="collectionOfValues[1][2]" type="text" value="f" />
    <button type="submit">Save</button>
}

コントローラ

public class SampleController : Controller {
    [HttpPost]
    public ActionResult Edit(IEnumerable<IEnumerable<string>> collectionOfValues) {
        foreach (var values in collectionOfValues) {
            Debug.WriteLine("[" + string.Join(", ", values) + "]");
        }
        // [a, b, c]
        // [d, e, f]

        return RedirectToAction(null);
    }
}

モデルのコレクションのコレクションにバインドする

こんな感じでできます。

ビュー

@using (Html.BeginForm()) {
    <input name="collectionOfModels[0][0].Id" type="text" value="1" />
    <input name="collectionOfModels[0][0].Name" type="text" value="a" />
    <input name="collectionOfModels[0][1].Id" type="text" value="2" />
    <input name="collectionOfModels[0][1].Name" type="text" value="b" />
    <input name="collectionOfModels[0][2].Id" type="text" value="3" />
    <input name="collectionOfModels[0][2].Name" type="text" value="c" />
    <input name="collectionOfModels[1][0].Id" type="text" value="4" />
    <input name="collectionOfModels[1][0].Name" type="text" value="d" />
    <input name="collectionOfModels[1][1].Id" type="text" value="5" />
    <input name="collectionOfModels[1][1].Name" type="text" value="e" />
    <input name="collectionOfModels[1][2].Id" type="text" value="6" />
    <input name="collectionOfModels[1][2].Name" type="text" value="f" />
    <button type="submit">Save</button>
}

モデルとコントローラ

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

    public override string ToString() {
        return string.Format(@"{{ Id = {0}, Name = ""{1}"" }}", Id, Name);
    }
}

public class SampleController : Controller {
    [HttpPost]
    public ActionResult Edit(IEnumerable<IEnumerable<SampleModel>> collectionOfModels) {
        foreach (var models in collectionOfModels) {
            Debug.WriteLine("[" + string.Join(", ", models) + "]");
        }
        // [{ Id = 1, Name = "a" }, { Id = 2, Name = "b" }, { Id = 3, Name = "c" }]
        // [{ Id = 4, Name = "d" }, { Id = 5, Name = "e" }, { Id = 6, Name = "f" }]

        return RedirectToAction(null);
    }
}

さすがMVC